I am trying to add an element to a quadtree in D3 but I am getting a "RangeError: Maximum stack size exceeded' error in the console whenever I add more than one element. My quadtree declaration looks like this (in CoffeeScript):
quadtree = d3.geom.quadtree()
.x((d) -> x_scale(d[dim_1]))
.y(0)
quadroot = quadtree([])
... later in another function (quadtree and quadroot are both scoped globally) ...
quadtree.extent([[x_scale.range()[0], 0],[x_scale.range()[1], 0]])
datapoints = datapoints_g.selectAll("circle")
.data(vis_data)
datapoints.exit()
.remove()
# now add / transition datapoints
datapoints.enter()
.append("circle")
.attr("cx", x_scale.domain()[0])
.attr("cy", y_scale.domain()[0])
.on("mouseover", showDetails)
.on("mouseout", hideDetails)
datapoints
.transition()
.duration(1000)
.attr("r", (d) ->
if sizeBy == 'none'
return datapoint_size
else
return size_scale(d[sizeBy]))
.attr("cx", (d) -> x_scale(d[dim_1]))
.attr("cy", (d) ->
if dim_2 != 'none'
y_scale(d[dim_2])
else
quadroot.add(d)
# calculateOffset(maxR)
)
I am attempting to modify the example from http://fiddle.jshell.net/6cW9u/8/, just FYI; however, I still get the infinite recursion error upon adding the second element to the quadroot even when I comment out all of the other code from the example.
Any guidance would be appreciated greatly!
Thanks in advance.
Related
I found a GitHub version of Mike Bostock's adjacency matrix visualization, using data from Les Miserables.
I transformed it into Vue3 code, but since this is my first project, I probably made some mistakes. The cells that should be added to each row, are added to the main HTML and the visualization is not appearing (black screen).
Here is an online version of the transformed visualization in Vue3 format:
https://codesandbox.io/s/sweet-gould-dejiuj?file=/src/components/Miserables.vue:33765-33857
This is because the context of the method roww() is not the proper context.
A row is created with the following code :
const row = this.svg
.selectAll(".row")
.data(this.matrix)
.enter()
.append("g")
.attr("class", "row")
.attr("transform", (d, i) => `translate(0,${this.x(i)})`)
.each(this.roww);
The last line call this.roww for each node of the selection, but the context of that function (this keyword inside this.roww) is somehow hardcoded to the object it is a member of, so it does not receive the proper context which should be the actual node object which relates to the DOM.
To fix that, you need to use a regular function created using function keyword (not an arrow functon for the same reason as above) so that it can be passed the correct context, though since your function precisely relies on its "parent" context (the other this), you will have to set a variable referring to it in the outer scope so it can be read within the function :
// ...
const that = this;
function fillrow (row) {
// eslint-disable-next-line no-unused-vars
const cell = d3.select(this).selectAll(".cell")
.data(row.filter((d) => d.z))
.enter()
.append("rect")
.attr("class", "cell")
.attr("x", (d) => that.x(d.x))
.attr("width", that.x.bandwidth())
.attr("height", that.x.bandwidth())
.style("fill-opacity", (d) => that.z(d.z))
.style("fill", (d) =>
that.get_nodes[d.x].group === that.get_nodes[d.y].group
? that.c(that.get_nodes[d.x].group)
: null
)
.on("mouseover", that.mouseover)
.on("mouseout", that.mouseout);
}
const row = this.svg
.selectAll(".row")
.data(this.matrix)
.enter()
.append("g")
.attr("class", "row")
.attr("transform", (d, i) => `translate(0,${this.x(i)})`)
.each(fillrow);
// ...
i have started with d3, on this moment i tray to edit a given script to meed the new requirments. I tray to reach a variabel but i can not access it. can someone help me out?
The problem is on line 5, i can not get "this.parentNode" but i need it.
Can someone point me in the right drection?
Thanks for your time
Greathings
function lines(x, yl, yr, n, svgElement, colors, Line) {
Line = Line || d3.line()
.x(function(d) { return x(d.time) + x.bandwidth() /2; })
.y(function(d) {
if(parseInt(d3.select(this.parentNode).attr("data-index")) !== scope.dataset.labels.indexOf(scope.rightas) && scope.rightas !== null) {
return yl(d.y);
}
return yr(d.y);
});
svgElement.selectAll('.line')
.data(function(d){ return [d]; })
.enter()
.append("path")
.attr("d", Line)
.attr("class", "line")
.style("fill", "none" )
.style("stroke", function(d, i, j) {
return d3.rgb(colors[d3.select(this.parentNode).attr("data-index")]).darker();
})
.style("stroke-width", "2")
}
You cannot get this DOM element from inside the line generator (with or without Angular). The line generator has no access to the element you're appending using it.
The API is clear about that:
When a line is generated, the x accessor will be invoked for each defined element in the input data array, being passed the element d, the index i, and the array data as three arguments. (emphasis mine)
The same, obviously, goes for the y accessor. So, as you can see, only the data is passed.
Therefore, this in this case will be simply the window, and there is no parentNode here.
Look at this demo (as the stack snippet takes a long time to console.log the window object, I'm using only this.name):
var line = d3.line()
.x(function(d) {
console.log(this.name);
return d;
})
.y(function(d) {
return d;
});
line([1]);
<script src="https://d3js.org/d3.v5.min.js"></script>
Similar to question here, I've been working on the same thing as in the last question. My question now is similar to the link, but is more about the implementation. When I run my code, I get an error in my log that says "TypeError: x.ticks is not a function". This is the piece of code it refers to:
svg.selectAll("g.grid")
.data(y.ticks()).enter()
.append("g").attr("class", "grid")
.selectAll("rect")
.data(x.ticks())
.enter()
.append("rect")
.attr("x", function(d, i, j) {
return xScale(j);
})
.attr("y", function(d, i, j) {
return yScale(i);
})
.attr("width", xScale.rangeBand())
.attr("height", yScale.rangeBand())
.attr("fill", function(d, i) {
return ((i) % 2) == 1 ? "green" : "blue";
});
This code works perfectly well in this fiddle, but gives me an error while running it in my code, as seen here. Any help?
In your example you are trying to call ticks on an ordinal scale instead of a linear scale. Utilising rangeBoundBands, like in the question you've linked to, is probably the way to go.
Your problem is you're using an ordinal scale. D3 makes you use a linear scale if you want to call .ticks(), otherwise it throws an error
I've created a plunk to demonstrate my problem.
The issue is that my .enter(),update(),exit() method is not working for my d3.chart.layout() visualization.
Instead, I get the classic "double post" problem. My keys are the same, however.
What I want to happen is for my d3 steam graph to update its data (such that the y values all go to 0, and the chart disappears). My data binding is coded normally:
var steam = svg.selectAll(".layer")
.data(layers, function(d){console.log(d); return d.key})
steam.enter().append("path")
steam.style("fill", function(d, i) { return z(i); }).style("opacity","0").transition().duration(400)
.style("opacity","1")
.attr("class", "layer")
.attr("d", function(d) { return area(d.values); })
steam.exit().transition().duration(500).remove()
What is happening/any ideas?
So I got it to work, though I'm still confused as to why it works this way. I needed to move the svg adding out of the update function and into the namespace (that was obvious).
But the update became this, with a transition() method. Can anyone help me understand where I went wrong?
test = svg.selectAll("path").data(layers, function(d){return d.key})
test.enter().append("path")
test.style("opacity",1).style("fill", function(d, i) { return z(i); })
.attr("class", "layer").transition()
.duration(2000)
.attr("d", function(d) { return area(d.values); });
test.exit().transition().duration(1500).style("opacity",0).remove();
I am trying to draw a pack layout in d3.js without outermost variable.
I want to draw a pack layout without outer most parent circle. Is there any way to do it?
Yes, there is. I would suggest following approach: you leave all circle pack initialization intact. You only change the point of code where circles are actually appended to DOM/SVG tree. I'll show this in a couple of examples. This jsfiddle is an example of "regular" circle pack:
The key code responsible for adding circles to the DOM tree is this:
var circles = vis.append("circle")
.attr("stroke", "black")
.style("fill", function(d) { return !d.children ? "tan" : "beige"; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return d.r; });
If one adds just this line between "vis" and ".append("circle")": (another jsfiddle available here)
.filter(function(d){ return d.parent; })
the root node will disappear:
If one adds this line:
.filter(function(d){ return !d.children; })
all nodes except leave nodes (in other words, those without children) will disappear:
And, a little bit more complex, this line
.filter(function(d){ return (d.depth > 1); })
will make the root parent circle and all its direct children disappear: