D3.js Focus+Context via Brushing Barchart - Recalculate BarWidth on Brushing - javascript

I some issue with a barchart with Focus+Context via Brushing. It's works pretty well but my problem is :
I calculate the barwidth with the bins and the width of my graph when I process my data.
var data = d3.layout.histogram()
.bins(x.ticks(bins))
(values);
var numBins = data.length;
var barWidth = parseInt((width)/numBins) - 1;
To be coherent, on brushing the barwidth on the focus graph should increase (currently it's keep the same width). So I need to recalculate the barwidth. But i have no idea how can i do this...
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focusGraph.attr("x", function(d, i) { return x(d.x); });
focusGraph.attr("width", barWidth); // How can I calculate new barwidth?
focus.select(".x.axis").call(xAxis);
}
Here the code :
http://jsfiddle.net/qcLp6qu8/

I find a solution if can help somebody :)
http://jsfiddle.net/sx9myywh/
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
var b = x(data[1].x)-x(data[0].x);
var w = b-2;
focusGraph.attr("x", function(d, i) { return x(d.x); });
focusGraph.attr("width", w);
focus.select(".x.axis").call(xAxis);
}

Try this code,
focusGraph.attr("width", width/(x.ticks().length));
x.ticks() will give the array of ticks, .length will give the number of ticks. To calculate the bar width. Total width by number of ticks this will give the space between the ticks which has to be bar width.
See update fiddle
Hope this will work for you, If not ask me for more...
Added a little code for better handling.

Related

D3 bar chart sorting - axis label sorting logic explanation needed

I've build bar chart with sorting on click: https://codepen.io/wawraf/pen/gvpXWm. It's based on Mike Bostock's chart https://bl.ocks.org/mbostock/3885705.
It works fine, but when I tried to build it from scratch I realized there is something i do not fully understand: Line 72 contains following function:
var x0 = scaleX
.domain(data.sort(sort(direction))
.map(function(d) { return d[0]; }));
So it's using variable scaleX defined before (Line 16), but when instead of "scaleX" variable I want to use raw d3 reference (which is actually the same as scaleX):
var x0 = d3.scaleBand().rangeRound([0, width - margin * 2])
.domain(data.sort(sort(direction))
.map(function(d) { return d[0]; }));
axis sorting ("g" elements) doesn't work.
I would be glad if anyone could explain why it doesn't actually work.
When you do...
var x0 = scaleX.domain(data.sort(sort(direction)).map(function(d) {
return d[0];
}));
... you are not only setting a new variable x0, but changing the scaleX domain as well. As the axis is based on scaleX, not x0, it won't do the transition in your second case, which only sets x0 (without changing scaleX).
You can certainly do:
var x0 = d3.scaleBand()
.rangeRound([0, width - margin * 2])
.domain(data.sort(sort(direction))
.map(function(d) {
return d[0];
}));
As long as you change the axis' scale:
xAxis.scale(x0);
here is the updated CodePen with those changes: https://codepen.io/anon/pen/VQveBy?editors=0010

How to handle collisions in Beeswarm plot in d3?

I've been playing around with this example here for a little while. What I'm trying to do is highlight a single node/circle in the plot (by making it larger with a border; later I want to add text or a letter inside it too).
Currently, I've made the circle for Bhutan larger in the plot like the following:
.attr("r",
function(d){return ( d.countryName === "Bhutan" ? r + 4 : r);})
.attr("stroke", function(d){if (d.countryName==="Bhutan"){return "black"}})
However, it overlaps with the other circles. What would be the best approach to avoid these collisions/overlaps? Thanks in advance.
Link to Plunkr - https://plnkr.co/edit/rG6X07Kzkg9LeVVuL0PH?p=preview
I tried the following to add a letter inside the bhutan circle
//find bhutan circle and add a "B" to it
countriesCircles
.data(data)
.enter().append("text")
.filter(function(d) { return d.countryName === "Bhutan"; })
.text("B");
Updated Plunkr - https://plnkr.co/edit/Bza5AMxqUr2HW9CYdpC6?p=preview
This is a slightly different problem than in this question here: How to change the size of dots in beeswarm plots in D3.js
You have a few options that I can think of:
Set the forceCollide to be your largest possible radius * 1.33, e.g. (r + 4) * 1.33. This will prevent overlapping, but spread things out a lot and doesn't look that great.
Add the radius property to each entry in your array and make the collide work based off that, which will look a bit better but not perform as awesomely for large sets.
Here's an example of how to do that:
...
d3.csv("co2bee.csv", function(d) {
if (d.countryName === "Bhutan") {
d.r = r + 4;
} else {
d.r = r;
}
return d;
}, function(error, data) {
if (error) throw error;
var dataSet = data;
...
var simulation = d3.forceSimulation(dataSet)
...
.force("collide", d3.forceCollide(function(d) { return d.r * 1.33; }))
...
countriesCircles.enter()
.append("circle")
.attr("class", "countries")
.attr("cx", 0)
.attr("cy", (h / 2)-padding[2]/2)
.attr("r", function(d){ return d.r; })
....
Use the row function in d3.csv to add a property to each member of the array called r, and check the country name to determine which one gets the larger value. Then use that value wherever you need to mess with the radius.
I guess it would've been possible to check the country name everywhere the radius was impacted (e.g. .force("collide", d3.forceCollide(function(d) { return d.countryName === "Bhutan" ? (r + 4) * 1.33 : r * 1.33; }), etc.). This feels a bit cleaner to me, but it might be cleaner still by abstracting out the radius from the data entries themselves...
Forked your plunk here: https://plnkr.co/edit/Tet1DVvHtC7mHz91eAYW?p=preview

Unable to get zoom by dragging a rectangle over a d3 series chart to work properly

I am trying to get zoom to work by dragging a rectangle over my series plot to identify the interval of zooming. Here is my plunkr
http://plnkr.co/edit/isaHzvCO6fTNlXpE18Yt?p=preview
You can see the issue by drawing a rectangle with the mouse over the chart - The new chart overshoots the boundary of the X and Y axes. I thought my group under the svg would take care of the bounds of the series (path) but I am clearly mistaken. After staring at it for a long time, I could not figure it out. Please ignore the angular aspect of the plunkr. I think the issue is somewhere in the
//Build series group
var series = svgGroup.selectAll(".series")
.data(data)
.enter().append("g")
.attr("class", "series");
//Build each series using the line function
series.append("path")
.attr("class", "line")
.attr("d", function (d) {
return line(d.series);
})
.attr("id", function (d) {
//While generating the id for each series, map series name to the path element.
//This is useful later on for dealing with legend clicks to enable/disable plots
legendMap[d.name] = this;
//Build series id
return buildPathId(d.name);
})
.style("stroke", function (d) {
//Use series name to get the color for plotting
return colorFcn(d.name);
})
.style("stroke-width", "1px")
.style("fill", "none");
Any help with this is appreciated.
Thank you very much.
I think the method renderChartWithinSpecifiedInterval(minX, maxX, minY, maxY, pixelCoordinates) maybe has some problem there.
It seems the parameter like max_x passed in line 130 are a very big value like time seconds
var svg = renderChartWithinSpecifiedInterval(min_X, max_X, min_Y, max_Y, false);
max_X,min_X are value like 1415171404335
min_Y = 0, max_Y = 100
But in dragDrop call in line 192
function gEnd(d,i){
svg.selectAll(".zoom-rect").remove();
var svgGp = svg.select("g");
var groupTransform = d3.transform(svgGp.attr("transform"));
var xOffset = groupTransform.translate[0];
var yOffset = groupTransform.translate[1];
var xBegin = Math.min(xStart,xDyn) - xOffset;
var xEnd = Math.max(xStart,xDyn) - xOffset;
var yBegin = Math.min(yStart,yDyn) - yOffset;
var yEnd = Math.max(yStart,yDyn) - yOffset;
renderChartWithinSpecifiedInterval(xBegin, xEnd, yBegin, yEnd, true);
//It seems here the parameters values are all pixels
like xBegin = 100, xEnd = 200
}
hope it helps!

set width and height of an SVG element - d3.js

I am using d3 to build up a graph.
Now, i am trying to set width and height of that area ike this -
var area2 = d3.svg.area()
.interpolate("monotone")
.attr("width", this.options.width)
.attr("height", 100);
But, i get this error --
Uncaught TypeError: undefined is not a function
See here -
NOTE: I have tested and this.options.width is not null - It's value is 1727.
I am new to d3 and SVG, any suggestion will help.
svg.area provides x, y0, and y1 (as was previously stated by In Code). y0 to y1 is bottom to the top - bottom of graph (x axis) to the line if it is a line graph.
Something like this sounds like what you want:
var area2 = d3.svg.area()
.x(function(d) { return x(d.X_VARIABLE); })
.y0(height)
.y1(function(d) { return y(d.Y_VARIABLE); });
Great example: http://www.d3noob.org/2013/01/filling-area-under-graph.html
As noted by the author, you should be defining your domains for x and y as well (if you have not).

NVD3 not dealing with large values

I have a graph implemented in NVD3, and I'm having serious trouble with it. NVD3 seems unable to handle datasets which contain large values. The graph is located here: http://brad.kizer.com.ng/. The code for the graph is as so:
nv.addGraph(function() {
// Get maximum and minimum Y values
var minY = 2 >> 30,
maxY = 0;
data.forEach(function (d) {
d.values.forEach(function (s) {
minY = Math.min(minY, s.y);
maxY = Math.max(maxY, s.y);
});
});
var chart = nv.models.stackedArea()
.forceY([0, 20])
.height($(container).closest('.chart').height())
.width($(container).closest('.chart').width());
(Object.prototype.toString.call(data) === '[object Array]') || (data = [data]);
d3.select(container)
.attr('width', $(container).closest('.chart').width())
.attr('height', 250)
.datum(data)
.transition().duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
I will appreciate any help so much, as this has kept me scratching my head for days.
Solved my problem. The issue was that the data provided for the Y-axis was strings, which made supposedly number addition become string concatenation:
"123" + "902" + "384" + "382" == "123902384382"; // (instead of 1791)
What I did was walk through the data and convert the strings to numbers.

Categories

Resources