Align multiple D3 circles in an arc or semi-circle pattern - javascript

This is one of my first d3 visualizations that's not a straightforward bar/line chart, so apologies if this question seems very basic. Here's the fiddle: http://jsfiddle.net/maureenlinke/jMMSJ/
I have about 100 data points, for each I'm generating a circle svg element. However, I'd like the circles to dynamically line up in the shape of an arc or half circle. I'm not sure the best way to go about this. To bind the circles to a path, or an arc? I want this to be dynamic so I don't want to specify each cx and cy coordinate. I played around with the transform/translate option also but that doesn't seem to work either. This could be a simple calculation but I just need to better understand the overall approach.
var svg = d3.select("#chart").append("svg");
var circles = svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("transform", "translate(220,400) scale(1, -1)")
.attr("cx", function(d,i){return ((i*10)-1);})
.attr("cy", function(d,i){return (i*10);})
.attr("r", 6.15)
.attr("transform", "translate(50,50)");

All you need is some simply trigonometry to determine the x and y coordinates for the circles:
.attr("cx", function(d,i){ return Math.cos(i / (data.length - 1) * cover) * circleRadius; })
.attr("cy", function(d,i){ return Math.sin(i / (data.length - 1) * cover) * circleRadius; })
Here, circleRadius is the radius of the circle the points should be on and cover is the arc segment they should cover (in radians). This will start the circle at the 3 o'clock position, which is probably not what you want in this case -- add an offset to make it a "top" arc.
Apart from that, you just need to translate the container element to accommodate the circles. Complete demo here.

Related

Centering Images in D3 After Transform

I'm trying to move an element in D3, in order to correspond to a circle underneath. Basically, when the user zooms on the page, the circles shrink (which allows them to remain visually appealing and separated).
I want to build a function that fires with the zoom event, that keeps the images centered within the circles. The circles are centered on their center points. However, as the images shrink, they appear to move to the left because their anchors are in the top-left corner.
I need a solution that might involve adding their sacrificed width and height to their relative "x" and "y" attributes. How would I implement a function like this? Or is there a better way?
The blockbuilder is here: http://blockbuilder.org/KingOfCramers/125cc79bce7dea48b21786b37302d258
Here is the relevant bit of code (the icon variable is the starting width of the image):
function zoom() {
var iconMove = icon/d3.event.transform.k;
g.attr("transform", d3.event.transform)
d3.selectAll(".storyImages")
.attr("width", `${iconMove}px`)
.attr("height", `${iconMove}px`)
.attr("x", // Keep this centered)
.attr("y", // Keep this centered)
d3.selectAll("circle")
.attr("r", function(){
return cirSize/d3.event.transform.k
})
}
Thanks for any help you can provide!
If you can position them to start, you can update them the same way on zoom, just with the new width/height of each item. You initially append each item with these attributes:
.attr("x", (d) => projection([d.lat,d.lon])[0] - icon/2)
.attr("y", (d) => projection([d.lon,d.lat])[1] - icon/2)
.attr("width", `${icon}px`)
.attr("height", `${icon}px`)
Which offsets the icon from the x,y values returned by the projection by half the icon's width and height - centering it on the projected point. Note: Your x value is set with d.lat, d.lon rather than d.lon, d.lat, also, your csv has lng, rather than lon as a header, so d.lng should be used).
To keep the icon centered on the point, just update the icon using the new icon width/height (which in your case is located in iconMove) and the new projected point:
.attr("x", (d) => projection([d.lng,d.lat])[0] - iconMove/2)
.attr("y", (d) => projection([d.lng,d.lat])[1] - iconMove/2)
.attr("width", iconMove)
.attr("height", iconMove);
Here's an updated block (I wasn't able to figure out how to save a new block builder block).

D3 v4 force graph with images as nodes

Having problems using images as nodes in a force-directed graph. Everything I've looked at so far seems to be v3 code and I haven't been able to get any images at all so far, whether using xlink:href or svg:image or both.
What's the right way to use an img as a node with v4?
Here's what I'm trying, and a jsfiddle.
As you can see in the CSS, I'm trying to get images from a spritesheet for each node.
var defs = d3.append("svg:defs");
defs.append("svg:pattern")
.attr("width", 48)
.attr("height", 48)
//.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", "http://placekitten.com/g/48/48")
.attr("width", 48)
.attr("height", 48)
.attr("x", 0)
.attr("y", 0);
var nodesDrawn = svg
.selectAll("node")
.data(nodesData)
.enter()
.append("g")
//.append("xhtml:i")
.append('circle')
.attr('r', 10);
The problems so far:
There is no d3.append in D3. You probably meant svg.append.
Set the width and the height of the <pattern> to 1.
Set an ID to the <pattern> and use that to fill the circles.
Here are the code with that changes (I'm increasing the circles' radii, so you can better see the image): https://jsfiddle.net/ppk23hnz/
By the way, none of those changes are related to D3 version: they are the same in v3.x and v4.x.
PS: Don't mix jQuery and D3. you don't need that (and it's quite disturbing).

Re-sizing circle SVG in D3

I'm trying to change the size of dots on a map in D3. Basically, just want to re-size the circle SVG in D3. I just want all the circles to be smaller, not trying to create proportional symbols. Here's my circles:
var centroid = map.selectAll(".centroid")
.data(centroid.features)
.enter()
.append("path")
.attr("d",path)
.attr("r", 100)
.attr("class", function(d){
return "centroid "+d.properties.info_city;
})
It's not working. I know that I can't do this in CSS, any thoughts on how to accomplish this in javascript as I'm trying to do? Thanks all!
Take a look at this plunker and let's move on from there: http://plnkr.co/edit/lKtCBKFS764MThajrqxX?p=preview
This map has similar centroids like yours. These are defined in:
g.selectAll(".centroid").data(centroids)
.enter().append("circle")
.attr("class", "centroid")
.attr("fill", fill)
.attr("stroke", stroke)
.attr("stroke-width", strokeWidth)
.attr("r", radius)
.attr("cx", function (d){ return d[0]; })
.attr("cy", function (d){ return d[1]; });
By changing .attr("r", radius) line (which is 6 in the config) to .attr("r", 2) you will get smaller circles
Here's the changed one: http://plnkr.co/edit/JUpMlnhZvZNacAIzI502?p=preview
I think you are trying to change the wrong part of the code since you should change the "r" of the circle elements not the "path" element ( I don't think path element even has a "r" attribute).
If your circles are drawn by a path then you must change the algorithm that draws those circles.
You are using the centroid coordinates to append a path as if it was a circle. This is not usual, the most common choice would be using a SVG circle, but it's OK... actually, Bostock does the same here.
The only problem is, as these are paths, not circles, changing the radius (r) will have no effect. So, to change the size of these "circles", this is what you have to do: find your d3.geo.path and add pointRadius to it.
var path = d3.geo.path()
.projection(projection)
.pointRadius(someValue);
Where someValue is, of course, some numeric value that fits your needs.
PS: for this to work properly, the types in your TopoJSON have to be "Point" or "MultiPoint".

d3 how to add a point to a chart using its coordinate space

I have a line-chart created with d3.js which shows two intersecting graphs (series of data-points). The intersection position gets calculated via javascript by using a lineIntersection-Algorithm and returns an x/y-object. I would like to create a circle at this position or a vertical line to show the break-even which gets visualized with this intersection.
Using d3 I wrote the following function to add a svg-circle to the chart:
d3.select('.nv-lineChart').append('circle')
.attr("cx", intersection.x)
.attr("cy", intersection.y)
.attr("r", 5.5)
.style("fill", "white")
.style("stroke", "black");
The problem is: the intersection coordinates need to be applied to the line-chart's coordinate-space.
I have no real clue how to do that - the d3 documentation is not really helpful for this.
Does anyone have a solution for this?

Why is there whitespace / "overlap" between rect elements where there shouldn't be?

I'm using d3.js to generate some rects which are directly above one another, in this fashion:
var greenRed = d3.select(".green-red").append("svg")
.attr("height", 120);
greenRed.append("rect")
.attr("fill", "green")
.attr("x", 0)
.attr("y", 0)
.attr("height", 50)
.attr("width", 300);
greenRed.append("rect")
.attr("fill", "red")
.attr("x", 0)
.attr("y", 50)
.attr("height", 50)
.attr("width", 300);
I've noticed that depending on which colours are stacked on top of one another, there is either a very thin whitespace present between the rectangles, or a sort of "overlap" of the two colours.
You can see what I mean in this fiddle: http://jsfiddle.net/ysim/PrC7X/
You can see that for .green-green and .green-grey there's no issue (to the naked eye, anyway); but for .green-blue and .red-blue, there is an overlap, and for .green-red, there is an extra whitespace.
I've tried adding .attr("stroke-rendering", "crispEdges") (suggested here) and .attr("stroke", "none") to the rect elements, as well as wrapping both the rect elements in a g element within the svg and adding .attr("stroke-rendering", "crispEdges") to that (suggested here), but neither of those solutions work.
What's causing this extra whitespace/overlap, and how do I go about fixing it so that the colours are neatly aligned, like in the first two cases?
try setting the stroke-width property to 0
That's antialiasing. Add style="shape-rendering: crispEdges" to the <div> elements and it will go away. You could add it to the shapes themselves instead if you want either as an attribute or a style.
The other thing to do is to add 0.5 to the y co-ordinates of your shapes There's more information about why that works here

Categories