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
Related
So, I want to render different symbols for node points on a tree graph. Which isn't too bad, I have the following code that can do that:
nodeEnter.append("path")
.attr("d", d3.svg.symbol()
.type(function(d) { if
(d.type == "cross") { return "cross"; } else if
(d.type == "rectangle") { return "rect";}
etc...
}));
The issue I have is, if you use append with a specific shape, for example append("circle"), you can specify the width, height, etc. With d3.svg.symbol, you can only specify the size. How can I dynamically use something like this:
nodeEnter.append("rect")
.attr("width", rectW)
.attr("height", rectH)
.attr("stroke", "black")
.attr("stroke-width", 1)
.style("fill", function (d) {
return d._children ? "lightsteelblue" : "#fff";
});
But also do it with dynamic shapes based on the node type attribute?
I tried something like:
nodeEnter.append(function(d){if
(d.type == "rectangle") { return "rect"; }});
However, this throws an error of:
TypeError: Argument 1 of Node.appendChild is not an object.
Most of the examples I have found while searching this don't bother trying to modify the symbol as long as they are all unique. Again, I want to be able to do something more complex.
Did not get any responses for this, but I was able to work something out. The answer is to use a raw input for the 'd' attribute and skip d3.svg.symbol altogether:
nodeEnter.append("path")
.attr("d", function(d) { if
(d.type == "circle") { return "M-40,0a40,40 0 1,0 80,0a40,40 0 1,0 -80,0";}
});
The caveat is, you need to draw your shapes manually with path.
I have a map of the USA that I'm trying to display lat/lon points over. I've mashed together a few examples to get this far, but I've hit a wall. My points are in a csv file, which I'm not sure how to upload here, but it's just 65,000 rows of number pairs. For instance 31.4671154,-84.9486771.
I'm mostly following the example from Scott Murray's book here.
I'm using the Albers USA projection.
var projection = d3.geo.albersUsa()
.scale(1200)
.translate([w / 2, h / 2]);
And setting up the landmarks as an svg group appended to the map container.
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h)
.on("click", stopped, true);
svg.append("rect")
.attr("class", "background")
.attr("width", w)
.attr("height", h)
.on("click", reset);
var g = svg.append("g");
var landmarks = svg.append("g")
I read the data and try to set circles at each lat/lon point.
d3.csv("./public/assets/data/landmark_latlon_edited.csv", function(error, latlon){
console.log(latlon);
landmarks.selectAll("circle")
.data(latlon)
.enter()
.append("circle")
.attr({
'fill': '#F00',
'r': 3
})
.attr('cx', function(d){
return projection([d.lon, d.lat][0]);
})
.attr('cy', function(d){
return projection([d.lon, d.lat])[1];
})
.style({
'opacity': .75
});
});
Now, the problem is that the cx property is not receiving a value. When viewed in the inspector the circles don't show a cx, and, indeed, appear in the svg at the appropriate y values, but in a stacked column at x=0.
<circle fill="#F00" r="3" cy="520.8602676002965" style="opacity: 0.75;"></circle>
I found an old issue I thought might be related here which states that the projection method will return null if you try to feed it values outside of its normal bounding box. I opened the csv in Tableau and saw a couple values that were in Canada or some U.S. territory in the middle of the Pacific (not Hawaii), and I removed those, but that didn't solve the problem.
I'm decidedly novice here, and I'm sure I'm missing something obvious, but if anyone can help me figure out where to look I would greatly appreciate it. Lots of positive vibes for you. If I can add anything to clarify the problem please let me know.
Thanks,
Brian
I had the same problem when I updated to d3 v3.5.6. Here is what I did to check for null values, so that you don't try to access the [0] position of null:
.attr("cx", function(d) {
var coords = projection([d.lon, d.lat]);
if (coords) {
return coords[0];
}
})
I'm sure there is a cleaner way to do this, but it worked for me.
You have a little error in your function generating cx values which messes it all up. It's just one parenthesis in the wrong place:
.attr('cx', function(d){
return projection([d.lon, d.lat][0]);
})
By coding [d.lon, d.lat][0] you are just passing the first value of the array, which is d.lon, to the projection and are returning the result of projection() which is an array. Instead, you have to place the [0] outside the call of projection() because you want to access the value it returned. Check your function for cy where you got things right. Adjusting it as follows should yield the correct values for cx:
.attr('cx', function(d){
return projection([d.lon, d.lat])[0];
})
I'm trying to place labels using the larskotthoff block page http://bl.ocks.org/larskotthoff/11406992
The issue I'm having is where the labels translate is worked out on the line.
.attr("transform", function (d) {
return "translate(" + projection(d.geometry.coordinates[0][0][0]) + ")";
})
So in his guide he is not doing d.geometry.coordinates[0][0][0] but just d.geometry.coordinates. This works for him because he is not working out the labels for multi-polygons because when I tried without the [0][0][0]
So my labels are not where they should be as you can see in the following image.
Although I've managed to get the labels drawing the structure of my feature (in this case d) is different and not a single array of coordinates. It is a 3 dimension array. So for this reason the placement (translate) is happening based on the first element [0][0][0] of each dimension. How do I overcome this? I want it to work out its placement from the whole set of polygons. Does d3 make something available for this?
UPDATE: Solution (thanks to Lars)
.data(json.features)
.enter().append("text")
.attr("class", "place-label")
.attr("transform", function (d) {
console.log("test");
return "translate(" + path.centroid(d) + ")";
})
.attr("x", function (d) {
return path.centroid(d)[0] > -1 ? 6 : -6;
})
.attr("dy", ".35em")
.style("text-anchor", function (d) {
return path.centroid(d)[0] > -1 ? "start" : "end";
})
As Lars Kotthoff confirms in comment. To get the centre location from a node/feature you can use path.centroid as described in the d3 documentation below.
Path.centroid
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 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.