I'm trying to do something like this: http://bost.ocks.org/mike/nations/
However instead of the transitions on mouseover I want the transitions to display when I click on a button for each year in the timeline.
Some example data in a csv file:
time,name,xAxis,yAxis,radius,color
1990,America,10,20.2,30,black
1990,China,50,50,50,yellow
2000,Singapore,20,30,20,red
2010,China,60,50,50,yellow
2020,America,20,30,40,black
2020,Malaysia,60,5,10,orange
I'm new to javascript and d3 and am having trouble with the transitions. I want the circles to be unique to each name (America, China, Singapore, Malaysia) so that I will only have one circle per name. Currently new circles add when I click on the respective timeline buttons, but don't transit to new positions or exit.
Read data using d3.csv:
d3.csv("data.csv", function(dataset) {
var years = [];
data=dataset;
//create a button for each year in the timeline
dataset.forEach(function(d){
console.log(d.time);
//if not existing button for timeline
if($.inArray(d.time, years) == -1)
{
var button = document.createElement("button");
button.setAttribute("type", "button");
button.setAttribute("class", "btn btn-default");
button.setAttribute('onclick', 'update("'+d.time+'")');
var t = document.createTextNode(d.time);
button.appendChild(t);
$("#timeline").append(button);
years.push(d.time);
}
})
//create circles for the first year
svg.selectAll("circles")
.data(dataset.filter(function(d) { return d.time == d3.min(years);}, function(d) { return d.name; }))
.enter()
.append("circle")
//.filter(function(d){ return d.time == d3.min(years); })
.attr("cx", function (d) { return d.xAxis *10; })
.attr("cy", function (d) { return d.yAxis; })
.style("fill", function(d) { return d.color; })
.transition()
.duration(800)
.attr("r", function(d) { return d.radius});
});
My update function:
function update(year){
var circle = svg.selectAll("circles")
.data(data.filter(function(d){return d.time == year;}), function(d) { return d.name; });
//update
circle.attr("class", "update")
.filter(function(d){ return d.time == year; })
.transition()
.duration(800)
.attr("cx", function (d) { return d.xAxis *10; })
.attr("cy", function (d) { return d.yAxis; })
.attr("r", function(d) { return d.radius});
//enter
circle.enter().append("circle")
.filter(function(d){ return d.time == year; })
.attr("cx", function (d) { return d.xAxis *10; })
.attr("cy", function (d) { return d.yAxis; })
.style("fill", function(d) { return d.color; })
.attr("r", function(d) { return d.radius});
//exit
circle.exit()
.remove();
}
Can someone point me in the right direction? Thanks.
svg.selectAll("circles") is invalid and should become svg.selectAll("circle") (singularize "circles").
As you have it currently, with "circles", it yields an empty selection, so d3 assumes all your data is bound to non-existent circles, and therefore the .enter() selection is always full (rather than being full only at the first render).
Next, in the section labled //update, you shouldn't need to do any filtering. The .data() binding you're doing to a filtered array should take care of this for you.
Also, the section labeled //create circles for the first year is unnecessary, and probably should be removed to eliminate side effect bugs. The update() function, assuming it's working fine, should take care of this for you.
Related
For a project I am attempting to have three visualizations for data based on car stats, where if you hover over one, the others will show the affects of that hovering as well.
The first is a bar graph, the second is a scatterplot, and the third is a line graph. For the line graph I wanted to group by manufacturer so that I don't have a couple hundred lines on my line graph, as the plot coordinates on the x and y are acceleration and model year. The other two don't need to be grouped in this way because one of their axes is the manufacturer.
I have the interactions from the line graph to the other two working since there is no nesting on the bar or scatterplot, and both the scatterplot and the bar graph can affect each other perfectly fine, but since the data is nested for the line graph, I can't seem to figure out how to access it, as the way I was doing it for the other two (using filtering) does not seem to work.
Below I am first showing where I am trying to create interactions when the mouse hovers (this is for the bar graph), and below that I include how my line graph is set up to show how it works. All I want is to make the corresponding line stand out more from the others by thickening the stroke when I hover over the bar or plot (in the scatterplot), and then go back to the normal size upon moving my cursor.
I followed the tutorial on the D3 website for line graphs, so there shouldn't be anything particularly wrong with that code.
Creating the bars for the bar graph, the mouseover and mouseout are the important parts:
var path1 = svg1.selectAll("myRect")
.data(data)
.enter()
.append("rect")
.attr("x", x1(0.1) )
.attr("y", function(d) { return y1(d.Manufacturer); })
.attr("height", y1.bandwidth() )
.attr("width", function(d) { return x1(d.Cylinders); })
.attr("fill", function (d) {
return color1(d.Cylinders);
})
.on('mouseover', function (d, i) {
svg1.selectAll('rect')
.filter(function(f) {
return f.Manufacturer === d.Manufacturer;
})
.attr("fill", function (d) {
return color4(d.Cylinders);
})
svg2.selectAll('circle')
.filter(function(f) {
return f.Manufacturer === d.Manufacturer;
})
.attr('r', 9)
.attr("fill", function (d) {
return color5(d.Horsepower);
});
svg3.selectAll('path') //THIS IS THE LINE GRAPH
.filter(function(f) {
console.log(this)
return ; // <-------This is where I don't know what to return to just get one line
})
.attr("stroke-width", 7)
})
.on('mouseout', function (d, i) {
svg1.selectAll('rect')
.filter(function(f) {
return f.Manufacturer === d.Manufacturer;
})
.attr("fill", function (d) {
return color1(d.Cylinders);
});
svg2.selectAll('circle')
.filter(function(f) {
return f.Manufacturer === d.Manufacturer;
})
.attr('r', 5)
.attr("fill", function (d) {
return color2(d.Acceleration);
});
d3.selectAll('path') //DELESLECTING LINE GRAPH
.filter(function(f) {
return f.key === d.Manufacturer; //this is what I tried before but it doesn't work
})
.attr("stroke-width", 1.5)
});
Creating the line graph:
var sumstat = d3.nest()
.key(function(d) { return d.Manufacturer;})
.entries(data);
// Add X axis
var x3 = d3.scaleLinear()
.domain([69, 84])
.range([ 0, width3 ]);
svg3.append("g")
.attr("transform", "translate(0," + height3 + ")")
.call(d3.axisBottom(x3).ticks(5));
// Add Y axis
var y3 = d3.scaleLinear()
.domain([8, d3.max(data, function(d) { return +d.Acceleration; })])
.range([ height3, 0 ]);
svg3.append("g")
.call(d3.axisLeft(y3));
var div3 = d3.select("#my_div").append("div")
.attr("class", "#tool_tip")
.style("opacity", 0)
.style("font-size", "xx-large");
// color palette
var res = sumstat.map(function(d){ return d.key }) // list of group names
var color = d3.scaleOrdinal()
.domain(res)
.range(['darkolivegreen','darkred','palevioletred','indianred', 'hotpink'])
// Draw the line
svg3.selectAll(".line")
.data(sumstat)
.enter()
.append("path")
.attr("fill", "none")
.attr("stroke", function(d){ return color(d.key) })
.attr("stroke-width", 1.5)
.attr("d", function(d){
return d3.line()
.x(function(d) { return x3(d.ModelYear); })
.y(function(d) { return y3(+d.Acceleration); })
(d.values)
})
.on('mouseover', function (d, i) {
//highlight;
svg3.selectAll("path")
.attr("stroke-width", 0.9)
d3.select(this)
.attr("stroke", function(d){ return color(d.key)})
.attr("stroke-width", 6)
svg1.selectAll('rect')
.filter(function(f) {
return f.Manufacturer === d.key;
})
.attr("fill", function (d) {
return color4(d.Cylinders);
})
svg2.selectAll('circle')
.filter(function(f) {
return f.Manufacturer === d.key;
})
.attr('r', 9)
.attr("fill", function (d) {
return color5(d.Horsepower);
});
})
.on('mouseout', function (d, i) {
svg3.selectAll("path")
.attr("stroke-width", 1.5)
d3.select(this)
.attr("stroke", function(d){ return color(d.key)})
.attr("stroke-width", 1.5)
svg1.selectAll('rect')
.filter(function(f) {
return f.Manufacturer === d.key;
})
.attr("fill", function (d) {
return color1(d.Cylinders);
})
svg2.selectAll('circle')
.filter(function(f) {
return f.Manufacturer === d.key;
})
.attr('r', 5)
.attr("fill", function (d) {
return color2(d.Horsepower);
});
});
Any assistance I can get would be greatly appreciated!!
I think I may have figured out the problem. It would seem that trying to filter the paths causes an issue because the x and y axes are also technically lines, and thus have paths that are null. I tried
svg3.selectAll('path')
.filter(function(f) {
console.log(f)
if(f!=null)
return f.key === d.Manufacturer;
})
.attr("stroke-width",7)
In the .on('mouseover') function, and it seems to be working. The issue was the nulls, not the actual accessing of the keys.
Still taking suggestions if there is a better way to do this!
I created this chart using D3 V5. Also, I have attached the sample data on the fiddle you can view by clicking here.
I've included the tick function code block which appends new domains for x and y scales and line/data on the path to slide left:
When the tick function executes, the line sort of rebuilds which makes it look like it bounces.
How can it be smooth, without a bounce at all when it rebuilds the line?
var tr = d3
.transition()
.duration(obj.tick.duration)
.ease(d3.easeLinear);
function tick() {
return setInterval(function() {
var newData = [];
var tickFunction = obj.tick.fnTickData;
if (tickFunction !== undefined && typeof tickFunction === "function") {
newData = tickFunction();
for (var i = 0; i < newData.length; i++) {
obj.data.push(newData[i]);
}
}
if (newData.length > 0) {
var newMaxDate, newMinDate, newDomainX;
if (isKeyXDate) {
newMaxDate = new Date(
Math.max.apply(
null,
obj.data.map(function(e) {
return new Date(e[obj.dataKeys.keyX]);
})
)
);
newMinDate = new Date(
Math.min.apply(
null,
obj.data.map(function(e) {
return new Date(e[obj.dataKeys.keyX]);
})
)
);
newDomainX = [newMinDate, newMaxDate];
} else {
newDomainX = [
d3.min(obj.data, function(d) {
return d[obj.dataKeys.keyX];
}),
d3.max(obj.data, function(d) {
return d[obj.dataKeys.keyX];
})
];
}
// update the domains
//x.domain([newMinDate, newMaxDate]);
if (obj.tick.updateXDomain) {
newDomainX = obj.tick.updateXDomain;
}
x.domain(newDomainX);
if (obj.tick.updateYDomain) {
y.domain(obj.tick.updateYDomain);
}
path.attr("transform", null);
// slide the line left
if (obj.area.allowArea) {
areaPath.attr("transform", null);
areaPath
.transition()
.transition(tr)
.attr("d", area);
}
path
.transition()
.transition(tr)
.attr("d", line);
svg
.selectAll(".x")
.transition()
.transition(tr)
.call(x.axis);
svg
.selectAll(".y")
.transition()
.transition(tr)
.call(y.axis);
// pop the old data point off the front
obj.data.shift();
}
}, obj.tick.tickDelay);
}
this.interval = tick();
That bounce is actually the expected result when you transition the d attribute, which is just a string.
There are several solutions here. Without refactoring your code too much, a simple one is using the pathTween function written by Mike Bostock in this bl.ocks: https://bl.ocks.org/mbostock/3916621. Here, I'm changing it a little bit so you can pass the datum, like this:
path.transition()
.transition(tr)
.attrTween("d", function(d) {
var self = this;
var thisd = line(d);
return pathTween(thisd, 1, self)()
})
Here is the forked plunker: https://plnkr.co/edit/aAqpdSb9JozwHsErpqa9?p=preview
As Gerardo notes, transitioning the d attribute of the path won't work very well unless you modfiy the approach. Here's a simple example of the sort of wiggle/bouncing that will arise if simply updating the d attribute of the path:
Pᴏɪɴᴛs ᴛʀᴀɴsɪᴛɪᴏɴɪɴɢ ᴀᴄʀᴏss sᴄʀᴇᴇɴ, ᴡɪᴛʜ ᴘᴀᴛʜ ᴛʀᴀɴsɪᴛɪᴏɴɪɴɢ ғʀᴏᴍ ᴏɴᴇ ᴅᴀᴛᴀ sᴇᴛ ᴛᴏ ᴛʜᴇ ɴᴇxᴛ.
The above behavior is noted by Mike Bostock in a short piece here, and here's a snippet reproducing the above animation:
var n = 10;
var data = d3.range(n).map(function(d) {
return {x: d, y:Math.random() }
})
var x = d3.scaleLinear()
.domain(d3.extent(data, function(d) { return d.x; }))
.range([10,490])
var y = d3.scaleLinear()
.range([290,10]);
var line = d3.line()
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y); })
var svg = d3.select("body")
.append("svg")
.attr("width",500)
.attr("height", 400)
.append("g");
var path = svg.append("path")
.datum(data)
.attr("d", line);
var points = svg.selectAll("circle")
.data(data, function(d) { return d.x; })
.enter()
.append("circle")
.attr("cx", function(d) { return x(d.x); })
.attr("cy", function(d) { return y(d.y); })
.attr("r", 5);
function tick() {
var transition = d3.transition()
.duration(1000);
var newPoint = {x:n++, y: Math.random() };
data.shift()
data.push(newPoint);
x.domain(d3.extent(data,function(d) { return d.x; }))
points = svg.selectAll("circle").data(data, function(d) { return d.x; })
points.exit()
.transition(transition)
.attr("cx", function(d) { return x(d.x); })
.attr("cy", function(d) { return y(d.y); })
.remove();
points.enter().append("circle")
.attr("cx", function(d) { return x(d.x)+30; })
.attr("cy", function(d) { return y(d.y); })
.merge(points)
.transition(transition)
.attr("cx", function(d) { return x(d.x); })
.attr("r", 5);
path.datum(data)
.transition(transition)
.attr("d", line)
.on("end", tick);
}
tick();
path {
fill: none;
stroke: black;
stroke-width: 2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
One solution to this wiggle/bounce is:
add an additional point(s) to the data,
redraw the line with the recently added to data array
find out the next extent of the data
transition the line to the left
update the scale and transition the axis
remove the first data point(s)
This is also proposed in Mike's article that I've linked to. Here would be a basic implementation with your code:
I've avoided a setInterval function by recursively calling the function at the end of the last transition:
function slide() {
// Stop any ongoing transitions:
d3.selectAll().interrupt();
// A transition:
var transition = d3.transition()
.duration(2000)
.ease(d3.easeLinear)
// 1. add an additional point(s) to the data
var newData = obj.tick.fnTickData();
obj.data.push(...newData);
// 2. redraw the line with the recently added to data array
path.datum(obj.data)
areaPath.datum(obj.data)
// Redraw the graph, without the translate, with less data:
path.attr("transform","translate(0,0)")
.attr("d", line)
areaPath.attr("transform","translate(0,0)")
.attr("d", area)
// 3. find out the next extent of the data
// Assuming data is in chronological order:
var min = obj.data[newData.length][obj.dataKeys.keyX];
var max = obj.data[obj.data.length-1][obj.dataKeys.keyX];
// 4. transition the line to the left
path.datum(obj.data)
.transition(transition)
.attr("transform", "translate("+(-x(new Date(min)))+",0)");
areaPath.datum(obj.data)
.transition(transition)
.attr("transform", "translate("+(-x(new Date(min)))+",0)");
// 5. update the scale and transition the axis
x.domain([new Date(min),new Date(max)])
// Update the xAxis:
svg.selectAll('.x')
.transition(transition)
.call(x.axis)
.on("end",slide); // Trigger a new transition at the end.
// 6. remove the first data point(s)
obj.data.splice(0,newData.length)
}
slide();
Here's an updated plunkr.
For example, I need to calculate a Math.sqrt of my data for each attr, how can I calculate only one time the Math.sqrt(d)?
var circle = svgContainer.data(dataJson).append("ellipse")
.attr("cx", function(d) {
return Math.sqrt(d) + 1
})
.attr("cy", function(d) {
return Math.sqrt(d) + 2
})
.attr("rx", function(d) {
return Math.sqrt(d) + 3
})
.attr("ry", function(d) {
return Math.sqrt(d) + 4
});
Has any elegant/performative mode? I'm thinking this way:
var aux;
var circle = svgContainer.data(dataJson).append("ellipse")
.attr("cx", function(d) {
aux = Math.sqrt(d);
return aux + 1
})
.attr("cy", function(d) {
return aux + 2
})
.attr("rx", function(d) {
return aux + 3
})
.attr("ry", function(d) {
return aux + 4
});
An underestimated feature of D3 is the concept of local variables which were introduced with version 4. These variables allow you to store information on a node (that is the reason why it is called local) independent of the data which might have been bound to that node. You don't have to bloat your data to store additional information.
D3 locals allow you to define local state independent of data.
Probably the major advantage of using local variables over other approaches is the fact that it smoothly fits into the classic D3 approach; there is no need to introduce another loop whereby keeping the code clean.
Using local variables to just store a pre-calculated value is probably the simplest use case one can imagine. On the other hand, it perfectly illustrates what D3's local variables are all about: Store some complex information, which might require heavy lifting to create, locally on a node, and retrieve it for later use further on in your code.
Shamelessly copying over and adapting the code from Gerardo's answer the solution can be implemented like this:
var svg = d3.select("svg");
var data = d3.range(100, 1000, 100);
var roots = d3.local(); // This is the instance where our square roots will be stored
var ellipses = svg.selectAll(null)
.data(data)
.enter()
.append("ellipse")
.attr("fill", "gainsboro")
.attr("stroke", "darkslateblue")
.attr("cx", function(d) {
return roots.set(this, Math.sqrt(d)) * 3; // Calculate and store the square root
})
.attr("cy", function(d) {
return roots.get(this) * 3; // Retrieve the previously stored root
})
.attr("rx", function(d) {
return roots.get(this) + 3; // Retrieve the previously stored root
})
.attr("ry", function(d) {
return roots.get(this) + 4; // Retrieve the previously stored root
});
<script src="//d3js.org/d3.v4.min.js"></script>
<svg></svg>
Probably, the most idiomatic way for doing this in D3 is using selection.each, which:
Invokes the specified function for each selected element, in order, being passed the current datum (d), the current index (i), and the current group (nodes), with this as the current DOM element (nodes[i]).
So, in your case:
circle.each(function(d){
//calculates the value just once for each datum:
var squareRoot = Math.sqrt(d)
//now use that value in the DOM element, which is 'this':
d3.select(this).attr("cx", squareRoot)
.attr("cy", squareRoot)
//etc...
});
Here is a demo:
var svg = d3.select("svg");
var data = d3.range(100, 1000, 100);
var ellipses = svg.selectAll(null)
.data(data)
.enter()
.append("ellipse")
.attr("fill", "gainsboro")
.attr("stroke", "darkslateblue")
.each(function(d) {
var squareRoot = Math.sqrt(d);
d3.select(this)
.attr("cx", function(d) {
return squareRoot * 3
})
.attr("cy", function(d) {
return squareRoot * 3
})
.attr("rx", function(d) {
return squareRoot + 3
})
.attr("ry", function(d) {
return squareRoot + 4
});
})
<script src="//d3js.org/d3.v4.min.js"></script>
<svg></svg>
Another common approach in D3 codes is setting a new data property in the first attr method, and retrieving it latter:
.attr("cx", function(d) {
//set a new property here
d.squareRoot = Math.sqrt(d.value);
return d.squareRoot * 3
})
.attr("cy", function(d) {
//retrieve it here
return d.squareRoot * 3
})
//etc...
That way you also perform the calculation only once per element.
Here is the demo:
var svg = d3.select("svg");
var data = d3.range(100, 1000, 100).map(function(d) {
return {
value: d
}
});
var ellipses = svg.selectAll(null)
.data(data)
.enter()
.append("ellipse")
.attr("fill", "gainsboro")
.attr("stroke", "darkslateblue")
.attr("cx", function(d) {
d.squareRoot = Math.sqrt(d.value);
return d.squareRoot * 3
})
.attr("cy", function(d) {
return d.squareRoot * 3
})
.attr("rx", function(d) {
return d.squareRoot + 3
})
.attr("ry", function(d) {
return d.squareRoot + 4
});
<script src="//d3js.org/d3.v4.min.js"></script>
<svg></svg>
PS: by the way, your solution with var aux will not work. Try it and you'll see.
I have a multiline graph that displays 10 series of data, I am trying to get the lines to update with new data but for some reason I can't get that happening.
The transition with the new data is working for the points on the lines so I assume I am not selecting the right elements but for the life of me I can't figure out where my mistake is.
At one point I had one line changing which indicated it was only updating from the first index of the data array.
Any insight would be appreciated:
Initial Series creation-
var series = svg.selectAll(".series")
.data(seriesData)
.enter().append("g")
.attr("class", "series");
series.append("path")
.attr("id", function (d) {
return d.name;
})
.attr("stay", "false")
.attr("class", "line")
.attr("d", function (d) {
d.line = this;
return line(d.values);
})
.attr("opacity", ".2")
.on("click", function () {
fadeOuts(this);
})
.style("stroke", function (d) {
return strokeCol;
})
.style("stroke-width", "4px")
.style("fill", "none");
Update function:
This is where I am stuck, the points respond to the new data but the paths do not.
series.data(newseriesData);
series.selectAll("path")
.attr("id", function (d) {
return d.name;
})
.attr("d", function (d) {
d.line = this;
return line(d.values);
})
.attr("opacity", ".2")
.on("click", function () {
fadeOuts(this);
})
.style("stroke", function (d) {
return strokeCol;
})
.style("stroke-width", "4px")
.style("fill", "none");
series.selectAll(".point")
.data(function (d) {
return d.values;
})
.transition()
.attr("cx", function (d) {
return x(d.label) + x.rangeBand() / 2;
})
.attr("cy", function (d) {
return y(d.value);
})
.style("fill", function (d) {
return color(d.name);
})
.style("stroke", "grey")
.style("stroke-width", "2px")
.on("mouseover", function (d) {
showPopover.call(this, d);
})
.on("mouseout", function (d) {
removePopovers();
})
Yes this is a university project, this is the last piece of work in a solid 50+ hour effort on this and I'd just like to get it knocked out.
The short answer is that instead of series.selectAll("path") you should use series.select("path"). Remember that series is already a selection, and the subselection is done for each element in it. You've appended exactly one element to each of the selection, so .select() is fine and no .selectAll() is required.
The main difference this makes is that .select() inherits the data from the parent selection, while .selectAll() doesn't -- when doing .selectAll() the data is simply not updated and therefore no change occurs.
first of all sorry for the long title, I couldn't come up with a better one to explain my problem.
What I have is a scatterplot, with circles that have different ids based on the d.FamilyName column in my csv. Some circles share the same id.
On the right of that scatterplot, I set up a div, containing a list of all the d.Familyname values, nested to have them show just once. The id of every text is also set by d.FamilyName.
To improve readability of the scatterplot, since it has many values, I was planning to add an event listener on mouseover on the text, which should then modify the radius of the circles sharing the same id as the text.
Every circle is plotted inside the var circle and the text is plotted inside a div, here's the code for the circle and the text part:
var circle = svg.append("g")
.attr("id", "circles")
.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function (d) { return x(d.SquadraturaInterna_o); })
.attr("cy", function (d) { return y(d.SquadraturaEsterna_o); })
.attr("r", 2)
After the radius attribute there's an event listener to show other values of the specified circle (the name and coordinates) but they are not relevant to my problem I think.
The text part:
d3.select("#elenco")
.select("#value")
.selectAll("text")
.data(nested)
.enter()
.append("p")
.append("text")
.attr("id", function (i) { return (i).key; })
.text(function (i) { return (i).key; })
.on("mouseover", function (d, i) {
if (this.id == circle.id)
{d3.select("circle")
.attr("r", 5);
}
else {d3.select("circle").attr("r", 1);}
;})
.on("mouseout", function (d, i) {
d3.selectAll("circle")
.attr("r", 2);
});
The problem is of course on the if statement in the mouseover.
Any hint on how to solve this would be really appreciated, thanks!
EDIT: Thanks to #Lars' reply I was able to make this work with a little edit to his code, like so:
.on("mouseover", function (d) {
var sel = this.id;
circle.filter(function() { return this.id === sel; }).attr("r", 5); })
.on("mouseout", function (d, i) {
d3.selectAll("circle")
.attr("r", 2); }
);
As an alternative to #musically_ut's approach, you can also use the .filter() function to modify only the elements you want.
.on("mouseover", function(d) {
circle.filter(function() { return this.id === d.FamilyName; }).attr("r", 5);
})
.on("mouseout", function(d) {
circle.filter(function() { return this.id === d.FamilyName; }).attr("r", 1);
})
I think you are looking for this:
.on("mouseover", function (d, i) {
circles.attr('r', function (circle_d, i) {
return (d.id == circle_d.id) ? 5 : 1;
});
})
In D3, the this in the accessor functions refers to the DOM element. If you wanted to compare the id of the DOM element with circle's data's ids, then you could do something like this kind: d3.select(this).attr('id') == circle_d.id (cache d3.select(this).attr('id') for performance reasons).