Using D3 ver 3.5.5. I am using an example (https://gist.github.com/stepheneb/1182434) as a template: the example code to draw the data looks like this:
var circle = this.vis.select("svg").selectAll("circle")
.data(this.points, function(d) { return d; });
circle.enter().append("circle")
.attr("class", function(d) { return d === self.selected ? "selected" : null; })
.attr("cx", function(d) { return self.x(d.x); })
.attr("cy", function(d) { return self.y(d.y); })
.attr("r", 10.0)
.style("cursor", "ns-resize")
.on("mousedown.drag", self.datapoint_drag())
.on("touchstart.drag", self.datapoint_drag());
circle
.attr("class", function(d) { return d === self.selected ? "selected" : null; })
.attr("cx", function(d) {
return self.x(d.x); })
.attr("cy", function(d) { return self.y(d.y); });
circle.exit().remove();
I think of this as four sections: the first does selectAll("circles") and adds the data. The second tells where the data points are ("cx", "cy") and other attr(), and the third is a bit of mystery to me, because it appears to also set "cx" and "cy", but no other attributes. Finally, we do and exit().remove(), which the documentation says removes any data elements not associated with the data array. I dont see how this is happening in this example. When I set breakpoints into the code, both the "cx" steps get called for each data point in the this.points array.
In my code, I try to do the same steps:
hr_circles = self.graph_gps.svg.selectAll("hr_circles")
.data(self.graph_gps.datay1); // , function(d){return d;}
hr_circles.enter().append("circle")
.style("z-index", 3)
.attr("class", "y1")
.attr("r", 1)
.attr("cx", function (d, i) {
return xScale(d.time)
})
.attr("cy", function (d, i) {
return yScale(d.vy)
})
.on("mouseover",
function (d) {...displays a tooltip...})
.on("mouseout", function (d) {
});
hr_circles.attr("class", "y1")
.attr("cx", function (d, i) {
return xScale(d.time)
})
.attr("cy", function (d, i) {
return yScale(d.vy)
})
hr_circles.exit().remove();
When my graph initially displays, the data appear just fine, properly scaled, etc. When I try to re-scale by dragging on the x-axis (as in the example), the axis rescales itself just fine, and re-scaled data appears on the graph, but the original data is also still there (no longer scaled correctly), making a big mess! How do you erase or make the originally scaled data go away?
Tried to post images, but I guess my reputation is too low. Will send to anyone interested.
Related
I tried to update an array of circles from the static positions
to dynamic force layout positions.
I tried to apply different colors for the circles within 'update' range
and apply another color to the new circles added from the new data.
however, newly added data nodes are not rendered for some reason.
Can anyone help me?
My update pattern is as below.
1.Initial Data Binding
let svg = d3.select('.graph')
.attr('width',width)
.attr('height',height)
svg.selectAll('circles').data(randoms)
.join('circle')
.attr('cx',d=>Math.random()*600)
.attr('cy',d=>Math.random()*600)
.attr('r',5)
2.Update Data binding
let simulation = d3.forceSimulation(randoms2)
.force('x', d3.forceX().x(function(d,i) {
if(i%2==0){
return 10;
}
else{
return 20
}
}))
.force('collision', d3.forceCollide().radius(function(d) {
return 1
}))
.on('tick',ticked)
function ticked(){
d3.selectAll('circle')
.data(randoms2)
.join( enter =>enter.append('circle')
.attr("fill", "green")
.attr('cx', function(d) {return d.x})
.attr('cy', function(d) {return d.y})
.attr('r',5),
update => update
.attr("fill", "red"),
exit => exit.remove()
)
.transition().duration(100)
.attr('cx', function(d) {return d.x})
.attr('cy', function(d) {return d.y})
}
It only shows the updated circles.
the complete code is in the following link.
https://codepen.io/jotnajoa/pen/xxEYaYV?editors=0001
I have 4 circles with a radius of 70px upon enter.
function draw_circles(...){
data = get_random_generador_data();
//the data contains the circle coordinates, as well
// as the radius, the text or the fill color.
var circleGroup = svgContainer.selectAll('g').data(data);
//=======
//ENTER
//=======
var circleGroupEnter = circleGroup
.enter()
.append('g')
.attr("id", function(d) { return "group_"+d["ix"]; })
circleGroupEnter
.append("circle")
.attr("cx", function(d) { return d["cx"]; })
.attr("cy", function(d) { return d["cy"]; })
.attr("r", function(d) { return d["rad"]; })
.attr("id", function(d) { return "circle_"+d["ix"]; }) //this is 70
.style("fill", function(d) { return d["fill_color"]; })
;
//=======
//UPDATE
//=======
circleGroup.select("circle")
.attr("cx", function(d) { return d["cx"]; })
.attr("cy", function(d) { return d["cy"]; })
.attr("r", function(d) { return 2.0* d["rad"]; })
.attr("id", function(d) { return "circle_"+d["ix"]; })
.style("fill", function(d) { return d["fill_color"]; })
AFTER ENTER
Now, if the user performs a click in one specific one of them, I change the radius of that one to 1.5*70 and call draw_circles again. This generates a new set of data so the update part will be called.
d3.select('#'+c_id)
.transition()
.duration(duration_till_next)
.attr("r", function(d) { return 1.50* d["rad"]; })
After this I get:
AFTER CLICK
This works as expected. Now, I have a setTimeout and after a couple of seconds, draw_circles gets called again from within itself, getting new data and triggering the update part of the d3 code above.
BUT! on the update part of the code, I change the radius to 140px, as you can see on the line above .attr("r", function(d) { return 2.0* d["rad"]; })
setTimeout(function(){ draw_circles(...);}, 2000);
AFTER UPDATE:
So as you can see, there is one element for which the radius update was not applied. However, all the other properties of the circle were changed, such as the coordinates in the image, the text inside of it or the fill color. Only the radius change is not observed.
Now it cannot be a coincidence that the property I happen to modify ad hoc is the very same one that doesn't get updated, but I cannot understand why.
Any ideas?
EDIT: Ok so the issue is here:
d3.select('#'+c_id)
.transition()
.duration(duration_till_next)
.attr("r", function(d) { return 1.50* d["rad"]; })
setTimeout(function(){ draw_circles(...);}, 2000);
The issue is that duration_till_next is also 2000. So what seems to be happening is that by the time the circles are updated, the circle has not finished transitioning. I would have expected that changing the circle radius is finished first, since it's supposed to be called first.
This makes the problem disappear:
.duration(0.9 * duration_till_next)
but this doesn't
.duration(0.99 * duration_till_next)
So there seems to be some sort of race going on.
I have this visualization and I'm trying to add fisheye view to the chart. I have tried adding it with the following lines in the plotData function but it doesn't happen:
var fisheye = d3.fisheye.circular()
.radius(120);
svg.on("mousemove", function () {
fisheye.focus(d3.mouse(this));
circle.each(function (d) {
d.fisheye = fisheye(d);
});
});
Any ideas on how to solve this?
Thanks!
First things first, your d3.timer never stops running. This is driving my machine crazy (cpu 100%) and killing the performance of the fishey. I'm really not sure what you are doing there, so ignoring that for a moment.
Your fisheye needs a little massaging. First, it expects your data pixel's positions to be stored in d.x and d.y attributes. You can fudge this in with when drawing your circles:
circle
.attr("cx", function(d, i) { d.x = X(d[0]); return d.x; })
.attr("cy", function(d, i){ d.y = Y(d[1]); return d.y; });
Second, you are plotting your data in multiple steps, so you need to select all the circles for the fisheye. And third, you forgot the code that actually makes the points grow and shrink:
svg.on("mousemove", function () {
fisheye.focus(d3.mouse(this));
// select all the circles
d3.selectAll("circle.data").each(function(d) { d.fisheye = fisheye(d); })
// make them grow and shrink and dance
.attr("cx", function(d) { return d.fisheye.x; })
.attr("cy", function(d) { return d.fisheye.y; })
.attr("r", function(d) { return d.fisheye.z * 4.5; });
});
Updated example.
I have been using d3 to create a multiline chart with focus and context brushing. Everything is going well except on the transition the dots at the data points with the tooltips are moving to a completely wrong position. I can't figure out what is causing this. Any help would be much appreciated. I attached the full code here and noted on the graph where I'm pretty sure the bug should be:
http://jsbin.com/osumaq/20/edit
When the button is clicked, a new json is passed to the graph to read.
The buggy block of code I think is this:
topicEnter.append("g").selectAll(".dot")
.data(function (d) { return d.values }).enter().append("circle").attr("clip-path", "url(#clip)")
.attr("stroke", function (d) {
return color(this.parentNode.__data__.name)
})
.attr("cx", function (d) {
return x(d.date);
})
.attr("cy", function (d) {
return y(d.probability);
})
.attr("r", 5)
.attr("fill", "white").attr("fill-opacity", .5)
.attr("stroke-width", 2).on("mouseover", function (d) {
div.transition().duration(100).style("opacity", .9);
div.html(this.parentNode.__data__.name + "<br/>" + d.probability).style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY - 28) + "px").attr('r', 8);
d3.select(this).attr('r', 8)
}).on("mouseout", function (d) {
div.transition().duration(100).style("opacity", 0)
d3.select(this).attr('r', 5);
});
Thank you very much.
What do you mean by tooltip ? Is it the window that appears when we hover on dots ? They seem fine. What I can see is that your dots are not moving while the lines are, and if I had to guess I would say your enter and update selections are mixed. If the dots are already on screen and you want to update their position (by calling your method update) you should have somthing along these lines :
// Bind your data
topicEnter.append("g").selectAll(".dot")
.data(function (d) { return d.values })
// Enter selection
topicEnter.enter().append("circle").attr("clip-path", "url(#clip)").attr("class", "dot");
// Update all the dots
topicEnter.attr("stroke", function (d) {
return color(this.parentNode.__data__.name)
})
.attr("cx", function (d) {
return x(d.date);
})
.attr("cy", function (d) {
return y(d.probability);
})
[...]
I've got a sortable heat map that I've created in D3 shown here: http://bl.ocks.org/umcrcooke/5703304
When I click on the year (column) the initial sort/transition works well, but subsequent clicks resorts, but without the transition. I'm having difficulty troubleshooting it. The code for the transition listed below:
I've set it up such that when the column text is clicked the update function executes:
.on("click", function(d,i) { return d3.transition().each(update(d));});
And the relevant pieces of the update function are:
function update(year) {
grid.selectAll('rect')
.transition()
.duration(2500)
.attr("y", function(d) { return (sortOrder[year].indexOf(d.Country))*cell.height; })
grid.selectAll(".cell_label")
.transition()
.duration(2500)
.attr("y", function(d) { return (sortOrder[year].indexOf(d.Country))*cell.height + (cell.height-cell.border)/2; })
d3.selectAll(".row_label")
.sort(function(a, b) {
return d3.ascending(+a[year], +b[year]);
})
.transition()
.duration(2500)
.attr("y", function(d, i) { return (i*cell.height) + (cell.height-cell.border)/2; });
}
I'm not sure what you're trying to do with d3.transition().each() in the handler, but you don't need it. Changing to:
.on("click", function(d,i) { update(d); });
fixes the problem. See fiddle: http://jsfiddle.net/nrabinowitz/Lk5Pw/