d3.js (d.geometry.coordinates) issue when placing labels - javascript

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

Related

Rectangles Instead of Ticks D3

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

D3 projection not setting cx property

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];
})

Understanding Issues With CSV to Bubble Chart Using D3.js

So I've been picking up D3.js to visualize some data I have, and I need a bit of help understanding why certain things happen.
I took the code from this example on the D3 site, and modified it to read from a flat CSV rather than a flattened JSON file. Here's what it looks like:
d3.csv("top-brands.csv", function(csvData) {
var data = { name: "brand", children: csvData };
var node = svg.data([data]).selectAll(".node")
.data(pack.nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
node.append("title")
.text(function(d) { return d.brand; });
node.append("circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return color(d.brand); });
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.brand; });
});
It seems to work quite well, in the sense that my bubbles all get drawn with the correct data, but there are two niggling issues that keep coming up:
It keeps drawing a large circle that encloses the other circles. It appears to be as large as the canvas allows (480px by 480px) but I have no idea how to make it go away. Is it because of how my data is processed?
If I try to do anything with "d.brand" in any way, it will tell me that d.brand is undefined. The example from the D3 site takes the title and gets the substring if the length is too long, but I can't seem to do that. Is it because of the way I'm pulling my data?
Appreciate any help! I can't really provide example CSVs because it's proprietary data, but it's just three columns: brand, count, and proportion, which is a string, an int, and a float (percentage) in that order. Dummy data would work just fine, I think.
Thanks SO!

How to add svg on top of another svg in d3,js

I am using d3.js to build a stacked bar graph. I am referring to this graph http://bl.ocks.org/mbostock/3886208
I want to add a small square of different color on the bars which have equal value. For example in this graph- if population of 25 to 44 Years and 45 to 64 Years is equal then i want to show a square of 10,10(width,height) on both bars related to CA. This is what I was doing but its not showing on the bar:
var equalBar = svg.selectAll(".equalBar")
.data(data)
.enter().append("g")
.attr("class", "equalBar")
.attr("transform", function(d){ return "translate(" + x(d.states) + ",0"; });
equalBar.selectAll("rect")
.data(function(d) { return d.ages;} )
.enter().append("rect")
.attr("width", 10)
.attr("y", function(d){
return y(d.y1);
})
.attr("height", function(d)
{ return 10; })
.style("fill", "green");
Thanks a lot for help.
What you're trying to do isn't really compatible with the D3 approach to data management. The idea of selections relies on the data items to be independent, whereas in your case you want to compare them explicitly.
The approach I would take to do this is to create a new data structure that contains the result of these comparisons. That is, for each population group it tells you what other groups it is equal to. You can then use this new data to create the appropriate rectangles.

D3: move circle between two different g elements in force layout

I have two g elements each containing circles. Circles are organized using force.layout. The g elements are transitioning.
You can see here: demo. Reduced code:
var dots = svg.selectAll(".dots")
.data(data_groups)
.enter().append("g")
.attr("class", "dots")
.attr("id", function (d) {
return d.name;
})
...
.each(addCircles);
dots.transition()
.duration(30000)
.ease("linear")
.attr("transform", function (d, i) {
return "translate(" + (150 + i * 100) + ", " + 450 + ")";
});
function addCircles(d) {
d3.select(this).selectAll('circle')
.data(data_circles.filter(function (D) {
return D.name == d.name
}))
.enter()
.append('circle')
.attr("class", "dot")
.attr("id", function (d) {
return d.id;
})
...
.call(forcing);
}
function forcing(E) {
function move_towards(alpha) {
...
}
var force = d3.layout.force()
.nodes(E.data())
.gravity(-0.01)
.charge(-1.9)
.friction(0.9)
.on("tick", function (e) {
...
});
force.start();
}
I need to move circle (for example id=1) from the first g element to the second one using transition.
Any suggestions are appreciated.
It can be done.
What I did was:
1) Use jquery to append the point to the target group
2) Use a transformation (no transition) to move the point back to its original location
3) Transition the point to its new location
The jQuery was used for the appendTo method. It can be removed and replaced with some pure Javascript stuff, but it's quite convenient.
I've got a partially working fiddle here. The green points work right, but something is going wrong with the blue ones. Not sure why.
In my view, transitions work on a single element. If an element changes its position in the DOM tree, from below one g to another, I can't think of a way to make that as one smooth transition because it's basically a binary split: Now there's an element under one g, now it's gone but there's another one somewhere else.
What I'd do in order to achieve what I think you want to do: Group everything under the same ´g´, assign color and translation individually, then change color and translation for that single element you want to change.
But don't take that as a reliable statement that you can't do it the way you originally wanted.

Categories