Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I am trying to build a line chart equilvant of this group chart - with legend toggling. I am not sure I've got the animation correct - and want to essentially make the charts sisters in terms of structure.
//group chart
http://jsfiddle.net/0ht35rpb/259/
//line chart
http://jsfiddle.net/0ht35rpb/262/
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) {
return "translate(" + x0(d.State) + ",0)";
})
.selectAll("rect")
.data(function(d) {
return keys.map(function(key) {
return { key: key, value: d[key]};
});
})
.enter().append("rect")
.attr("x", function(d) {
return x1(d.key);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("width", x1.bandwidth())
.attr("height", function(d) {
return height - y(d.value);
})
.attr("fill", function(d, i) {
return z(d.key);
});
-- so the line one looks like this - but I think I am missing enter() parts
// define the line
var valueline = d3.line()
.curve(d3.curveBasis)
.x(function(d) {
return x(parseTime(d.date));
})
.y(function(d) {
return y(d.temperature);
});
g.selectAll(".city")
.data(cities)
.enter().append("g")
.attr("class", "city")
.append("path")
.attr("class", "line")
.attr("d", function(d){
return valueline(d.values);
})
.style("stroke", function(d) {
return z(d.id);
});
also when it comes to toggling the legends how do I fix the line chart to animate the lines - and fix the domains - as one is now a timescale. Also in reference to both charts - should I place the "make bars" "make lines" code seen above - into an actual function - that gets reused during the update function method - for each chart?
Here's a fiddle that has the animations as per your requirement.
Relevant code:
y.domain([
d3.min(cities, function(c) {
if(filtered.indexOf(c.id) === -1) {
return d3.min(c.values, function(d) {
return d.temperature;
});
}
}),
d3.max(cities, function(c) {
if(filtered.indexOf(c.id) === -1) {
return d3.max(c.values, function(d) {
return d.temperature;
});
}
})
]);
g.select(".axis.axis--y").transition().duration(500)
.call(d3.axisLeft(y));
g.selectAll('.city path').transition().duration(500).attr('d', function(d) {
if(filtered.indexOf(d.id) > -1) {
return null;
} else {
return valueline(d.values);
}
});
Your code was missing a lot of things:
X axis is a time scale, and in your case, you don't need to update the x-axis in the update function as your toggling based on the city names and not the dates.
Event if you had a changing X-axis, you wouldn't change the domain that way. Time scale looks for dates and it seems like you were setting that to the city names. ("newKeys")
Setting the y-domain is to be based on the "cities" array as you are using that to render the chart. But in the update function, you seem to be using "data" array to set the y domain and hence the y-axis issue.
Added transition to the y-axis too.
var paths = svg.selectAll(".line").selectAll("path") is not what you want as you have the class "line" for the path itself. A relevant version would be: svg.selectAll("path.line")
Anyway, the filtering of the paths wouldn't work as the selection was wrong and as far as the filtering is concerned, the paths are calling the "line" function whereas the line function in your code is defined as "valueline"
Filtering of the paths in a similar fashion as yours would be correct in this way:
g.selectAll('.city path').transition().duration(500)
.attr('d', function(d) {
if(filtered.indexOf(d.id) > -1) {
return null;
} else {
return valueline(d.values);
}
});
Hope this helps. :)
Related
I have a grouped bar chart design that has each bar amount immediately above the bar instead of the y axis bar on the far left.
I have added the text like so...
svg.selectAll(".text")
.data(data)
.enter()
.append("text")
.attr("class","label")
.attr("x", function(d) { return x1(d.rate) })
//.attr("y", function(d) { return y(d.value) + 1; })
.attr("dy", ".75em")
.text(function(d) {return d.value; });
But i cant seem to obtain the x and y values according to the bar position and the actual text of the d.value isn't getting inserted.
Sample here:
https://jsfiddle.net/6p7hmef1/7/
That's because the data bound to the texts that you're adding isn't right.
If you add a console.log as follows, you'll be able to see why the labels aren't being inserted. Check for the outputs in console. Console log fiddle
.text(function(d) {console.log(d); return d.value; });
One approach would be to append the texts to the bar groups ("slice" in your case). Just like you do for the bars. Here's a fiddle doing this:
JS Fiddle Demo
slice.selectAll(".text")
.data(function(d) { return d.values; })
.enter()
.append("text")
.attr("class","label");
slice.selectAll('text.label')
.attr("x", function(d) { return x1(d.rate)+ x1.rangeBand()/3 })
.attr("y", function(d) { return y(d.value) + 1; }).attr('dy', '-0.4em')
.text(function(d) {return d.value; });
This might look weird as the texts are shown before the transition of the bars. So changing the value of texts once the bars are shown seems like the right approach to me. (dx and dy attributes can be adjusted as per the requirement)
Here's the final fiddle:
JS FIDDLE
I'm using the "end" callback for every transition and changing the text values accordingly.
.each('end', function () {
d3.select(this.parentNode).selectAll('text.label')
.attr("x", function(d) { return x1(d.rate)+ x1.rangeBand()/3 })
.attr("y", function(d) { return y(d.value) + 1; }).attr('dy', '-0.4em')
.text(function(d) {return d.value; });
})
Hope this helps. :)
Let me know if any part of the code isn't understandable.
guys, I am working with the most recent beeswarm visualization by Mike Bostock.
I wonder, in D3js v.4, how to change the color of one single specific point. In this case, I am talking about unemployment in more than 180 countries, and I would like to single out only "Brazil" with a different color.
How can I pick Brazil's dot and make it with a different color? As last resort, I suppose I could use SVG to single it out, but its to big a code, burden. I am fairly new to D3.
Here is the code: http://codepen.io/voltdatalab/pen/KzrNGo
var cell = g.append("g")
.attr("class", "cells")
.selectAll("g").data(d3.voronoi()
.extent([[-margin.left, -margin.top], [width + margin.right, height + margin.top]])
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.polygons(data)).enter().append("g");
cell.append("circle")
.attr("r", 5)
.attr("cx", function(d) { return d.data.x; })
.attr("cy", function(d) { return d.data.y; })
;
Here is what I want to do:
I did this manipulating the SVG, not D3 code
You need to add .attr("fill",function(d) { if(d.data.id === "Djibouti")return "red"; }) to circle. Following is the change you need to make:
cell.append("circle")
.attr("r", 5)
.attr("cx", function(d) { return d.data.x; })
.attr("cy", function(d) { return d.data.y; })
.attr("fill",function(d) { if(d.data.id === "Djibouti")return "red"; })
;
Replace Djibouti with Brazil.
Here is a bin. Let me know if you want something like this.
Using D3 ver 3.5.5. I am using an example (https://gist.github.com/stepheneb/1182434) as a template: the example code to draw the data looks like this:
var circle = this.vis.select("svg").selectAll("circle")
.data(this.points, function(d) { return d; });
circle.enter().append("circle")
.attr("class", function(d) { return d === self.selected ? "selected" : null; })
.attr("cx", function(d) { return self.x(d.x); })
.attr("cy", function(d) { return self.y(d.y); })
.attr("r", 10.0)
.style("cursor", "ns-resize")
.on("mousedown.drag", self.datapoint_drag())
.on("touchstart.drag", self.datapoint_drag());
circle
.attr("class", function(d) { return d === self.selected ? "selected" : null; })
.attr("cx", function(d) {
return self.x(d.x); })
.attr("cy", function(d) { return self.y(d.y); });
circle.exit().remove();
I think of this as four sections: the first does selectAll("circles") and adds the data. The second tells where the data points are ("cx", "cy") and other attr(), and the third is a bit of mystery to me, because it appears to also set "cx" and "cy", but no other attributes. Finally, we do and exit().remove(), which the documentation says removes any data elements not associated with the data array. I dont see how this is happening in this example. When I set breakpoints into the code, both the "cx" steps get called for each data point in the this.points array.
In my code, I try to do the same steps:
hr_circles = self.graph_gps.svg.selectAll("hr_circles")
.data(self.graph_gps.datay1); // , function(d){return d;}
hr_circles.enter().append("circle")
.style("z-index", 3)
.attr("class", "y1")
.attr("r", 1)
.attr("cx", function (d, i) {
return xScale(d.time)
})
.attr("cy", function (d, i) {
return yScale(d.vy)
})
.on("mouseover",
function (d) {...displays a tooltip...})
.on("mouseout", function (d) {
});
hr_circles.attr("class", "y1")
.attr("cx", function (d, i) {
return xScale(d.time)
})
.attr("cy", function (d, i) {
return yScale(d.vy)
})
hr_circles.exit().remove();
When my graph initially displays, the data appear just fine, properly scaled, etc. When I try to re-scale by dragging on the x-axis (as in the example), the axis rescales itself just fine, and re-scaled data appears on the graph, but the original data is also still there (no longer scaled correctly), making a big mess! How do you erase or make the originally scaled data go away?
Tried to post images, but I guess my reputation is too low. Will send to anyone interested.
I've got a sortable heat map that I've created in D3 shown here: http://bl.ocks.org/umcrcooke/5703304
When I click on the year (column) the initial sort/transition works well, but subsequent clicks resorts, but without the transition. I'm having difficulty troubleshooting it. The code for the transition listed below:
I've set it up such that when the column text is clicked the update function executes:
.on("click", function(d,i) { return d3.transition().each(update(d));});
And the relevant pieces of the update function are:
function update(year) {
grid.selectAll('rect')
.transition()
.duration(2500)
.attr("y", function(d) { return (sortOrder[year].indexOf(d.Country))*cell.height; })
grid.selectAll(".cell_label")
.transition()
.duration(2500)
.attr("y", function(d) { return (sortOrder[year].indexOf(d.Country))*cell.height + (cell.height-cell.border)/2; })
d3.selectAll(".row_label")
.sort(function(a, b) {
return d3.ascending(+a[year], +b[year]);
})
.transition()
.duration(2500)
.attr("y", function(d, i) { return (i*cell.height) + (cell.height-cell.border)/2; });
}
I'm not sure what you're trying to do with d3.transition().each() in the handler, but you don't need it. Changing to:
.on("click", function(d,i) { update(d); });
fixes the problem. See fiddle: http://jsfiddle.net/nrabinowitz/Lk5Pw/
I have the multi series line chart code with slight modifications to support my data set. This is what I wish to do, and no solution I have looked at seems to function properly for me. I wish to overlay some element (circle, rectange, hidden, whichever) over each point on the line such that I could then attach a mouseover element on that point to display a box with data containing the d.time, d.jobID and how much that differs from an average. If possible, I would like the solution to only do this to the main line (the varying line) rather than the two lines drawn to represent the average. Here, I have a picture of the graph as-is for visual inspection. If that doesn't work, I have also attached it.
I have posted a bit the code below:
d3.tsv("values.tsv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "time" && key !== "jobID"; }));
data.forEach(function(d) {
d.time = parseDate(d.time);
d.jobID = parseInt(d.jobID);
});
var points = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {time: d.time, jobID: d.jobID, value: parseFloat(d[name],10)};
})
};
});
....
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 7)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("mbps");
var point = svg.selectAll(".point")
.data(points)
.enter().append("g")
.attr("class", "point");
point.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
point.append("text")
.datum(function(d) { return {name: d.name, jobID: d.jobID, value: d.values[d.values.length - 1]}; })
.attr("transform", function(d) { return "translate(" + x(d.value.time) + "," + y(d.value.value) + ")"; })
.attr("x", 6)
.attr("dy", ".7em")
.text(function(d) { return d.name; });
});
I have already tried the following code just to see if it worked with my implementation:
point.append("svg:circle")
.attr("stroke", "black")
.attr("fill", function(d, i) { return "black" })
.attr("cx", function(d, i) { return x(d.time) })
.attr("cy", function(d, i) { return y(d.value) })
.attr("r", function(d, i) { return 3 });
D3.JS seems like a pretty awesome piece of work, and I'm fortunate to have it.
EDIT: jsfiddle
The trick is to pass the data again to a selection and then operate on the result of that. Have a look at Mike's tutorial for some background and examples.
I've changed your jsfiddle to add circles here. Attaching svg:title elements or doing something else to show more information should be straightforward. Note that I modified your code to create the data points slightly to include the name with each element. This way, only one additional level of selections is necessary (treat all the points the same and add them in a single pass). The cleaner way to solve this from a code design point of view would be to have 2 additional levels -- first have a selection for the points for an individual line (and add an svg:g element to group them) and then add the points within this group. This would make the code quite a bit more complex and difficult to understand though.