I am trying to create a bar graph with a time scale where its possible to zoom into any time period. I was able to create the zooming functionality for my x-axis(time scale) however my data (in this case rects) doesn't zoom along with my axis.
Here is a simplified version of my graph: http://jsfiddle.net/gorkem/Mf457/5/embedded/result/
As you can see I can zoom in to my x-axis but the bars do not zoom along.
and here is the jfiddle link: http://jsfiddle.net/gorkem/Mf457/6/
var y = d3.scale.linear().domain([0, max_val]).range([graph_height, 0]);
var x = d3.time.scale().domain([minDate, maxDate]).range([0, graph_width]);
var chart = d3.select(location).append("svg")
.attr("class", "chart")
.attr("width", graph_width+20)
.attr("height", graph_height+20)
.call(d3.behavior.zoom().x(x).scaleExtent([1, 8]).on("zoom", zoom));
var lines = chart.selectAll("line");
var lines_y = lines
.data(x.ticks(5))
.enter().append("line")
.attr("x1", x)
.attr("x2", x)
.attr("y1", function (d) {return graph_height - 20 - d;})
.attr("y2", graph_height)
.style("stroke", "#ccc");
var lines_x = lines
.data(y.ticks(10))
.enter().append("line")
.attr("x1", 0)
.attr("x2", graph_width)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#ccc");
xAxis = d3.svg.axis().scale(x);
yAxis = d3.svg.axis().scale(y).orient("left");
chart.append("svg:g")
.attr("class", "xaxis")
.attr("transform","translate(0,300)")
.call(xAxis);
chart.append("svg:g")
.attr("class", "yaxis")
.attr("transform", "translate(25,0)")
.call(yAxis);
var rect = chart.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function (d,i) {return x(new Date(d["date"]))+20; })
.attr("y", function (d,i) { return graph_height - (d["num"] *v_scale);})
.attr("width", x(new Date(data[1]["date"])))
.attr("height", function (d,i) {return d["num"]*v_scale;});
rect.call(d3.behavior.zoom().x(x).scaleExtent([1, 8]).on("zoom", zoom));
function zoom() {
chart.select(".xaxis").call(xAxis);
chart.select(".yaxis").call(yAxis);
}
}
I feel like I should be adding more functionality to my zoom function for the function to be effective on bars(rects). I would really appreciate any help.
Using 'selectAll', I've applied the scaling and translation to each bar respectively, restricting both scale and translation to the x-axis only.
function zoom() {
chart.select(".xaxis").call(xAxis);
chart.select(".yaxis").call(yAxis);
chart.selectAll(".chart rect").attr("transform", "translate(" + d3.event.translate[0] + ",0)scale(" + d3.event.scale + ", 1)");
}
This works equally well with a single path element, which is what I was trying to figure out when I stumbled upon your question.
I'm wondering the same thing, so at least know you're in good company. Here's my current approach. First of all, I can't generate a plot from your code and the links don't send me to anything useful -- no chart renders. So I can't help with your specific problem. However, there is an example that has zoomable rectangles here: http://bl.ocks.org/1962173. I'm currently going through it and deleting unnecessary elements and seeing if I can make it work. Hopefully this helps!
Related
I wanted to create a circular crop an image to use in conjunction with a svg circle, and decided on filling the svg circle with a pattern. I have it working for the single case:
defs.append("svg:pattern")
.attr("id", "story1")
.attr("width", 200)
.attr("height", 100)
.attr("patternUnits", "userSpaceOnUse")
.append("svg:image")
.attr("xlink:href", 'ml-image4.png')
.attr("width", 200)
.attr("height", 100)
.attr('x',0)
.attr('y',0);
var circle1 = svg.append("circle")
.attr("cx", 100)
.attr("cy", 50)
.attr("r", 40)
.style("fill", "#fff")
.style("fill", "url(#story1)")
.style('stroke','#000')
.style('stroke-width', 5);
Then I got all gung-ho and tried to adapt for a programmatic implementation, but it didn't pan out so well. I'm hoping there is a way to do it, because my end goal is to have an application that uses many pictures as patterns. It will be like a little gallery where each svg circle is spaced out across the page using .attr('x', function(d,i) { return 100+i*200})) and each circle referencing a different pattern. The pattern id is a data member, so I was trying to do this: .style('fill',function(d) {return "url(#"+d.id+")"; }). I'm not exactly sure why it didn't work; to my eyes it seems functional-ish. I did get the error:
Uncaught DOMException: Failed to execute 'querySelectorAll' on
'Element': 'svg:pattern' is not a valid selector.
Here is my adaptation for programmatic def patterns and a quick look at my data:
var data = [
{'id':'story1', 'Title':'Title1', 'Date':'03/10/2017','Icon':'Icon1.png', 'Link':'www.link1.com/'},
{'id':'story2', 'Title':'Title2', 'Date':'12/15/2017','Icon':'Icon2.png', 'Link':'www.link2.com/'}
];
var defs = svg.append('svg:defs');
defs.selectAll(null)
.data(data)
.enter()
.append('svg:pattern')
.attr('id',function(d) {return d.id; })
.attr('width', 200)
.attr('height', 100)
.attr('patternUnits', 'userSpaceOnUse')
.append('svg:image')
.attr('xlink:href', function(d) {return d.Icon})
.attr('width', 200)
.attr('height', 100)
.attr('x',0)
.attr('y',0);
svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', function(d,i) {return 100+i*200})
.attr('cy', 50)
.attr('r',40)
.style('fill',function(d) {return "url(#"+d.id+")"; })
.style('stroke','#000')
.style('stroke-width',3);
Question
Judging by the error, my adaptation seems to be flawed. What can I tweak to get programmatic patterns for svg circles here? In my humble opinion, my approach isn't all that different from the simple case at the very beginning of the post (1 pattern 1 circle), all I was trying to do was scale it up so I can use d3.selectAll() using my data.
Please look at Hexbin and scatterplots: http://imgur.com/a/2oR68
Why in Hexbinplot, points donot touch each other whereas in scatterplot it clearly touches the close points ?
I expected my hexbin plot comes up like this: https://bl.ocks.org/mbostock/4248145 but it didnot.
I am using d3.hexbin plugin.
The only code that is differing from Hexbin plot to scatter plot (I am dealing with same dataset) apart from little bit of scaling is:
For Hexbin:
var color = d3.scale.linear()
.range(["white", "steelblue"])
.interpolate(d3.interpolateLab);
var hexbin = d3.hexbin()
.extent([[0,0],[size - padding , padding]])
.radius();
hexbin.x(function(d,i){return x(subdata[0][i]);})
hexbin.y(function(d,i){return y(subdata[0][i]);})
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("class", "mesh")
.attr("width", w)
.attr("height", size);
svg.append("g")
.attr("clip-path", "url(#clip)")
.selectAll(".hexagon")
.data(hexbin(datum))
.enter()
.append("path")
.attr("class", "hexagon")
.attr("d", hexbin.hexagon())
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.style("fill", function(d) { return color(d.length); });
For scatterplot:
svg.selectAll("circle")
.data(datum)
.enter()
.append("circle")
.style("fill", "steelblue")
.attr("cx", function (d, i) {
return x(subdata[0][i]);
})
.attr("cy", function (d,i) {
return y(subdata[0][i]);
})
.attr("r", 3)
Where am i doing wrong ?
Edit1: Included some fraction of code under Hexbin
If you set...
.attr("d", hexbin.hexagon(5))
//radius value here ------^
..., the hexagons will touch only if you set the same value in the hexabin generator:
var hexbin = d3.hexbin()
.radius(5)//same value here
.extent([[0, 0], [width, height]]);
According to your result, I believe that was not the case. Thus, the solution can be simply removing that value:
.attr("d", hexbin.hexagon())
//no radius here --------^
My initial array contains quarterly data, needed to build a boxplot, faceted by ProspRating. I created number of complex g objects (one for each ProspRating) based on the first quarter data. Each g incapsulates simple boxplot elements - lines, rectangles, etc.)
To create the objects I used the following code (excerpt for one simple boxplot element (line.range) only):
d3.select("svg").selectAll("g.box")
.data(data10Q1)
.enter()
.append("g")
.attr("class", "box")
.attr("transform", function(d) {return "translate(" + xScale(d.ProspRating) +"," + yScale(d.v_mdn) + ")"})
.each(function(d,i) {
d3.select(this)
.append('line')
.attr("class", "range")
.attr("x1", 0)
.attr("x2", 0)
.attr("y1", yScale(d.v_max) - yScale(d.v_mdn))
.attr("y2", yScale(d.v_min) - yScale(d.v_mdn))
.style("stroke", "black")
.style("stroke-width", "4px");
The initial boxplot based on the code works well. Currently am trying to write a function to update the element attributes based on data for another quarter.
Excerpt from the code (for one boxplot's element (line.range) only):
function update (quarter) {
var filtered = data.filter(function(d) {
return d.quarter === quarter})
var boxes = d3.select("svg").selectAll("g.box")
.data(filtered, function(d){return d.ProspRating})
.attr("transform", function(d) {return "translate(" + xScale(d.ProspRating) +"," + yScale(d.v_mdn) + ")"})
.each(function(d,i) {
d3.select(this)
.select("line.range")
.attr("y1", yScale(d.v_max) - yScale(d.v_mdn))
.attr("y2", yScale(d.v_min) - yScale(d.v_mdn))
Looks like d3.select(this).select("line.range") does not work properly. What would be the best way to access the line.range and other similar elements to update their attributes?
I guess you missed the .enter() after var boxes = d3.select [...] .data [...]
In order to draw a baton that its starting point takes coordinates of arc's center in a Pie Chart d3 js, How can determinate coordinates of each arcs's center ?
I tried this code but only the first baton was in the right position.
var lines = arcs.selectAll("line")
.data(data)
.enter()
.append("line")
.attr("x1", function(d, i) { return r*Math.cos( parseFloat(d)/r);})
.attr("y1", function(d) { return r*Math.sin( parseFloat(d)/r) ;})
.attr("x2", function(d) { return r*Math.cos( parseFloat(d)/r) + parseFloat(d) ;})
.attr("y2", function(d) { return r*Math.sin( parseFloat(d)/r) + parseFloat(d) ;})
.attr("class", "line")
.style("stroke", function (d) {return color(d.data) ; })
.style("stroke-width", "3px");
Please i need your help.
As pointed out in the comments, the arc.centroid() function provides this functionality:
var pie = d3.layout.pie().value(function(d){ return d.value; });
var arc = d3.svg.arc().innerRadius(0).outerRadius(r);
var centers = pie(data).map(arc.centroid);
Or with a D3 selection:
var centers = [];
arcs.each(function(d) {
centers.push(arc.centroid(d));
});
Complete demo here.
I have a line (& area) graph which works ok, when horizontal. But I really need it to be vertical, to stand up. I have tried all kinds of changes to the existing code. Mostly with some strange results.
Here’s the code (modified it a bit). Is there a way to change it to make the graph vertical?:
var x = d3.scale.linear().domain([1, itemCount]).range([0, width]);
var y = d3.scale.linear().domain([0, maxValue]).rangeRound([height, 0]);
// Set up linar x and y axis.
var xAxis = d3.svg.axis().scale(x).ticks(10);
var yAxis = d3.svg.axis().scale(y).ticks(2).orient("left");
// Line graph.
line = d3.svg.line()
.interpolate("basis")
.x(function (d) {
return x(d.x);
})
.y(function (d) {
return y(d.y);
});
// Create SVG element.
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
// X-Axis, to bottom.
svg.append("svg:g")
.attr("class", "axis")
.attr("transform", "translate(1," + height + ")")
.call(xAxis);
//Y-Axis
svg.append("svg:g")
.attr("class", "axis")
.attr("transform", "translate(40, 1)")
.call(yAxis);
// Horizontal axis guide lines.
svg.selectAll("line.y")
.data(y.ticks(5))
.enter()
.append("line")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#000000")
.style("stroke-opacity", 0.1);
// Vertical axis guide lines.
svg.selectAll("line.x")
.data(y.ticks(5))
.enter()
.append("line")
.attr("x1", x)
.attr("x2", x)
.attr("y1", 0)
.attr("y2", this.heightInside - pushDown)
.style("stroke", "#000000")
.style("stroke-opacity", 0.1);
// Set up domains. Nice ensures the domains ends on nice round values.
x.domain([dataValues[0].x, dataValues[dataValues.length - 1].x]).nice();
y.domain([d3.min(dataValues, function (d) { return (d.y); }),
d3.max(dataValues, function (d) { return (d.y); })])
.nice();
// Draw line on graph.
svg.append("svg:path")
.attr("class", "line")
.attr("d", line(dataValues))
.style("stroke", function(d) { return colors[i]; });
// Marks.
svg.selectAll("circle_" + i)
.data(dataValues)
.enter()
.append("circle")
.style("fill", function(d) { return _this.colors[i]; })
.attr("r", 4)
.attr('cx', function (d) { return x(d.x); })
.attr('cy', function (d) { return y(d.y); });