line d3.js graph shifting wrongly (to right) - javascript

Because I am having an issue with my own code, I am studying below link:
https://bost.ocks.org/mike/path/
I think I get below
// push a new data point onto the back
data.push(random());
// redraw the line, and then slide it to the left
path
.attr("d", line)
.attr("transform", null)
.transition()
.attr("transform", "translate(" + x(-1) + ")");
// pop the old data point off the front
data.shift();
But the part that doesn't work for me and I don't understand is
.attr("transform", "translate(" + x(-1) + ")");
My code's x is like below:
var x = d3.time.scale().range([-5, width])
domain is not known at this time and later domain is defined as
x.domain(d3.extent(callbackData, function(d){return d.reg_date;}));
I try to use x(-1) but entire graph(except axis) disappears when I call for update so I use something like below and initially, it seems to work.(graph shifts to the left). But as more data comes in(btw, my data comes in and updates the data properly(shift off the front data and push latest into the back) graph starts to shifting towards the right. I can see graph start taking points from the front(which is correct) but problem is, graph is starting to shift to right(instead of left).
Really beating my head w/ this issue so hopefully someone can kindly advise.
path
.attr("d",line)
.attr('transform', null)
.transition()
.duration(300)
.ease('linear')
.attr("transform", "translate(" + -2 + ")");

This is a quick implementation : https://jsfiddle.net/thatOneGuy/j2eovk9k/8/
I have added this to work out distance between the x ticks :
var testtickArr = y.ticks(10);
var testtickDistance = y(testtickArr[testtickArr.length - 2]) - y(testtickArr[testtickArr.length - 1]);
Then used this as the distance to move the path :
path
.attr("d", line)
.transition()
.duration(1000)
.ease('linear')
.attr("transform", function(d) {
console.log(d)
return "translate(" + (pathTrans) + ")";
})
Also, update the translation value :
pathTrans -= testtickDistance;
The way you're updating your data can be improved. But this should help you with the transition. This is still not working ok at all as the distances between your points are not equal. For yours to work like the example you need equal distances between each points so it transitions to the left smoothly.

Related

How can I spread the paths in the y axis?

I have 14 series of lines. I would like to show them with a spacing between them. something like in the image, I think the best option is with the translate property. I do not know how to do it with each line. I'm trying to get my data between 0 and 100
http://plnkr.co/edit/qmkxYEJYpIkXUQUMQMDa?p=preview
paths.attr('transform', null)
.transition()
.duration(duration)
.ease('linear')
.attr('transform', 'translate(' + x(now - (limit - 1) * duration) + ')')
.each('end', tick)
Use a scale band (or v3 ordinal scale with rangeBand) to translate the lines, and the scale linear to draw the lines
http://plnkr.co/edit/qs1i4sVzBXl6de7b8HJ3?p=preview
var band = d3.scale.ordinal()
.domain(ids)
.rangeBands([height, 0])
and
for (var name in groups) {
var group = groups[name];
let g = paths.append("g")
.attr("transform", function(d,i){
return "translate(0, " + band(name) +")";
});
group.path = g.append('path')
.data([group.data])
.attr('class', name + ' group')
.style('stroke', group.color)
}
Your problem right now is that horrendous for...in loop. Do not use loops to append elements in a D3 code. It's not only unnecessary but it will also make things very difficult to change, like your situation right now.
Thus, since you have that loop (I'll not refactor the code to eliminate that, it's simply too much work for a — free — S.O. answer), this is my solution:
First, increase the height of the chart and decrease the range of the y scale. Here, I'm setting the height to 800 and the range to [45, 0]. Change that accordingly (or, if you want to avoid magic numbers — always a good idea — have a look at the other answer)
Then, in the for loop, translate each element according to a counter, here named index:
var index = 0;
for (var name in groups) {
var group = groups[name]
group.path = paths.append('path')
.data([group.data])
.attr('class', name + ' group')
.style('stroke', group.color)
.attr("transform", function() {
return "translate(0," + (55 * index++) + ")";
})
}
This is your plunker with those changes: http://plnkr.co/edit/NPhWMpcorXhAubwytmOe?p=preview

D3 Categorical Area Chart - Scale Issue

I'm trying to create an area chart for statistics on each US state. I have a single number statistic for each state; an element of my data list looks like the following:
{'state':'CA','count':4000}
Currently, my area chart looks like this. The task is mainly complete, but you may notice how the very last category (in this case, UTAH) isn't filled. I'm not quite sure how to get around this. close_up
I am using a scaleBand axis; this felt appropriate. Perhaps it is not the correct approach. Here is the JS behind the chart:
var svg_area = d3.select("#area")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom),
g_area = svg_area.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand().range([0, width]),
y = d3.scaleLinear().range([height, 0]);
var area = d3.area()
.x(function(d) { return x(d.state); })
.y0(height)
.y1(function(d) { return y(d.count); });
d3.csv('data/states.csv', function(data) {
data.forEach(function(d) {
d.count = +d.count;
});
data.sort(function(a, b){
return b.count-a.count;
});
data = data.slice(0,30);
x.domain(data.map(function(d) { return d.state; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]);
g_area.append('path')
.datum(data)
.attr('fill', solar[1])
.attr("class", "area")
.attr('d', area);
g_area.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g_area.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(0," + 0 + ")")
.call(d3.axisLeft(y));
});
Any suggestions on how I can fix this? Thanks for any feedback!
Contrary to your question's title (now edited), the area chart is not "leaving out the last data point".
What you're seeing is the expected result, since you are using a band scale. Actually, that value just above the horizontal axis (just in the "edge" of the area chart) is Utah value! Try to understanding it with this explanation: Imagine a bar chart with your data. Each bar has, of course, a given width. Now, draw a path going from the top left corner of one bar to the top left corner of the next bar, starting at the first bar and, when reaching the last bar, going down from the top left corner to the axis. That's the area you have right now.
There are two solutions here. The first one is using a point scale instead:
var x = d3.scalePoint().range([0, width])
However, this will trim the "margins" of the area path, before the first state and after the last state (Utah). That means, the area chart will start right over California tick and end right over Utah tick.
If you don't want that there is a second solution, which is hacky, but will keep those "margins": add the bandwidth() to the last state in the area generator:
var area = d3.area()
.x(function(d, i) {
return i === data.length - 1 ?
x(d.state) + x.bandwidth() : x(d.state)
})
It may be worth noting that, using a band scale, your chart is technically incorrect: the values in the area for each state are not over the tick for that state.

How to move labels to outside pie chart in D3

I'm trying to recreate this in D3
I've got my D3 code here: http://codepen.io/jpezninjo/pen/XpoVwQ
I can't figure out how to move labels to outside my pie chart. I know it's this line
.attr("transform", function(d) {
return "translate(" + labelArc.centroid(d) + ")"; })
but I'm having a hard time looking for information about centroid. I'm guessing it's taking the center between labelArc's inner and outer radius, but I tried messing with that and got no difference.
Try this
.attr("transform", function(d) {
var c = labelArc.centroid(d);
return "translate(" + c[0]*1.2 +"," + c[1]*1.2 + ")";
})
You can play with 1.2 which allows you to position the labels outside the pie chart.

How to apply drag behavior to a d3.svg.arc?

How do I apply d3.behavior.drag() to the following arc?
var arc = d3.svg.arc()
.innerRadius(50)
.outerRadius(70)
.startAngle(45 * (pi/180)) //converting from degs to radians
.endAngle(3) //just radians
vis.append("path")
.attr("d", arc)
.attr("transform", "translate(200,200)")
I want to be able to drag the arc around. I have not been able to see anything that uses the drag behavior on any SVG path based object (only for basic elements like circle, rectangle, etc.)
The closest thing I can find related to dragging is this:
http://bl.ocks.org/mbostock/1557377
Though it appears that if you try to ".on("drag", dragmove) for the appended path (.append("path")) "d" comes out as undefined. And if you attach ".on("drag", dragmove)" to the arc itself, the event doesn't appear to fire...)
Drag is a behaviour that you create and then apply to the elements you want to execute that behaviour. There should be no issue applying it to an arc.
So with your arc (minor modification to make the translation accessible):
var position = [200,200];
var arc = vis.append("path")
.attr("d", arc)
.attr("transform", "translate(" + position + ")");
Start by creating the behavior you want. Our drag will update the translation of the arc:
var drag = d3.behavior.drag()
.on("drag", function(d,i) {
position[0] += d3.event.dx;
position[1] += d3.event.dy;
d3.select(this)
.attr("transform", function(d,i){
return "translate(" + position + ")"
})
});
Now we attach the behaviour to the arc:
arc.call(drag);
You can try it yourself here.

Display an SVG image at the middle of an SVG path

I have a force directed graph with different size nodes. I want to display a custom icon in the middle of each path connecting two nodes. From the d3 examples I found the way to display images within the nodes. However, when I try the same technique on the paths, the images are not shown.
var path = svg.append("svg:g").selectAll("path").data(force.links());
var pathEnter = path.enter().append("svg:path");
pathEnter.attr("class", function(d) {
return "link " + d.target.type;
})
pathEnter.append("svg:g").append("image")
.attr("xlink:href","http://127.0.0.1:8000/static/styles/images/add.png")
.attr("x",0).attr("y",0).attr("width",12).attr("height", 12)
.attr("class", "type-icon");
I guess I need a bit more patience before asking a question. The way I solved the problem is:
var icon = svg.append("svg:g").selectAll("g")
.data(force.links()).enter().append("svg:g");
icon.append("image").attr("xlink:href","imagePath")
.attr("x", -20)
.attr("y", -2)
.attr("width", 12).attr("height", 12)
.attr("class", "type-icon");
And then in the tick function:
icon.attr("transform", function(d) {
return "translate(" +((d.target.x+d.source.x)/2) + "," +
((d.target.y+d.source.y))/2 + ")";
});
to get the center point between the two nodes.

Categories