Create rectangle instead of bubbles in d3js - d3.js

I used this http://bl.ocks.org/mbostock/4063269 to create circles, but i would need to create rectangle instead of circle and all those rectangles needs to packed inside one bigger rectangle. please help.

This is how you can make rectangles instead of circles.
node.append("rect")
.attr("width", function(d) { return d.r*2; })//width is the diameter
.attr("x", function(d) { return d.r*-1; })
.attr("y", function(d) { return d.r*-1; })
.attr("height", function(d) { return d.r*2; })//height is the diameter
.style("fill", function(d) { return color(d.packageName); });
Now to create a rectangle which packs all of them
//get all the data
var data = d3.selectAll("g")[0].map(function(d) {
return d3.select(d).data()[0]
})
//get the min x max x min y max y
var xmin = d3.min(data, function(d){return d.x-d.r})
var xmax = d3.max(data, function(d){return d.x+d.r})
var ymin = d3.min(data, function(d){return d.y-d.r})
var ymax = d3.max(data, function(d){return d.y+d.r})
//make a rectangle to make it over other rectangles.
svg.insert("rect" ,":first-child")
.attr("x", xmin)
.attr("y", ymin)
.attr("height", ymax-ymin)
.attr("width", xmax-xmin)
.style("opacity", 0.3)
.style("fill","blue");
working example here

Related

d3js add volume bars to line graph

I built this line graph with d3js: jsfiddle
Example Data:
{
date: '18-May-18',
close: 281755783529,
volume: 11792035643,
notes: null
}
How can I use the "volume" value from data to plot a rectangle bar corresponding to each date? The line graph with volume bars would look something like this:
Thank you!
Because the volume uses a different Y-range you need to define a new yScale for that
var yVolume = d3.scaleLinear().range([height, 0]);
yVolume.domain([0, d3.max(data, function(d) { return d.volume; })]);
Draw a right axis for this scale
var yAxisVol = d3.axisRight(yVolume).ticks(10);
svg.append("g")
.attr("class", "yaxisVol")
.attr("transform", `translate(${width},0)`)
.call(yAxisVol);
Then draw the rect on the correct location
svg.append("g").attr("class", "barsVol")
.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("fill", "yellow")
.attr("x", function(d) { return x(new Date(d.date.getTime()-11*60*60*1000)); })
.attr("y", function(d) { return yVolume(d.volume); })
.attr("height", function(d) { return height - yVolume(d.volume); })
.attr("width", function(d) {
return x(new Date(d.date.getTime()+11*60*60*1000)) -
x(new Date(d.date.getTime()-11*60*60*1000)); });
The rectangles have a width of 1 "day" (22 hours).
Add a clip rectangle to the barsVol group or extend the x-domain with a day on the left and right.

Append element (rect) with variable width after each x-axis coordinate position from previous element

I want to append each rect element exactly after previous element x-axis coordinate. My current code is:
var data = [50,100,150]
var svg = d3.select("#bar_chart")
.append("svg")
.attr("width", "1000")
.attr("height", "500")
var rect = svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d) { return (d * i) })
.attr("y", "300")
.attr("width", function(d, i){ return d})
.attr("height", function(d, i){ return d})
.attr("fill", "blue")
.each(function(d, i) {console.log(i.x)})
Which gives the following:
Code with x-axis position set to return (d * i)
What I would like:
Code with x-axis from each element start immediately after the previous one
Thanks in advance
You need to add all the widths prior to your current width.
https://jsfiddle.net/8dv1y74e/
var data = [50,100,150]
var svg = d3.select("#bar_chart")
.append("svg")
.attr("width", "1000")
.attr("height", "500")
var rect = svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", getPreviousWidths)
.attr("y", "300")
.attr("width", function(d, i){ return d})
.attr("height", function(d, i){ return d})
.attr("fill", "blue")
.each(function(d, i) {console.log(i.x)})
function getPreviousWidths(d,i){
return data.slice(0,i).reduce((memo,num)=>num+memo,0)
}
An alternative method to the other answer, you can use a variable to keep track of the x coordinate for the most recent rectangle, adding to it each time you append a rectangle:
var data = [50,100,150];
var svg = d3.select("body")
.append("svg")
.attr("width", "500")
.attr("height", "500");
var positionX = 0; // where we are on the x axis, first element should be at 0 pixels.
var rect = svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d) {
var x = positionX; // The x value for this rectangle
positionX = d + x; // The x value for the next rectangle
return x; // Return the x value for this rectangle.
})
.attr("y", "10")
.attr("width", function(d, i){ return d})
.attr("height", function(d, i){ return d})
.attr("fill", "blue");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

in d3js bar chart i want to have fixed bar width

http://bl.ocks.org/d3noob/8952219
I want to have bar size width to be fixed..
from above example i have changed the code from
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
to
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.date); })
.attr("width", 50)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
but the labels are not moving to proper place also bars are getting overlapped
You have to change the range() of your x scale, to fit with your bar width value:
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
to (if you want 50px as bar width)
var x = d3.scale.ordinal().range([0, data.length * 50]);
The range() method is used to define the display space for your scale.
I was looking for a similar solution. What #JulCh gave as an answer did not work out of the box for me, but lead me in the right direction.
Try:
var x = d3.scale.ordinal()
.range(d3.range(data.length).map(function (d) { return d * 50; }));
Where the inner d3.range creates an array containing the number of elements determined by data.length or some constant number (the number of bars you would like displayed).
Example: If data.length or some constant is 8 then [0,1,2,3,4,5,6,7] is returned from d3.range(8)
The map function then multiplies your fixed width of 50 against each element in the array returning [0,50,100,150,200,250,300,350].
D3 will then use these exact values to place your bars.

reverse how vertical bar chart is drawn

How can I reverse how my vertical bar is drawn?
I want it to be drawn from the bottom to the top.
It is important to maintain the right representation of the dataset
https://jsfiddle.net/adai183/ztsh1ptx/
var dataset = [ 10, 80, 5, 5];
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("fill", "rgb(250, 128, 114)")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return h - (d * 4);
})
.attr("width", w / dataset.length - barPadding)
.attr("height", 0)
.transition().duration(750).ease("linear")
.attr("height", function(d) {
return d * 4;
});
Do it like this:
.attr("y", function(d) {
return h; //set y to max height
})
.attr("width", w / dataset.length - barPadding)
.attr("height", 0)//height of the bars initially is 0
.transition().duration(750).ease("linear")//on transition
.attr("y", function(d) {
return d*4;//set the y to its value
})
.attr("height", function(d) {
return h - (d * 4);//set the height to max height - y's value
})
working code here
EDIT
For solving that problem make a scale for y:
var y = d3.scale.linear()
.range([h, 0])
.domain([0,100]);//since values vary between 0 and 100
Now use this scale to give height to your bar chart.
.transition().duration(750).ease("linear")
.attr("y", function(d) {
return y(d);
})
.attr("height", function(d) {
return h - y(d);
})
working example here
Hope this helps!

d3.js Changing gridlines and blocks to be evenly spaced

I'm playing with the following d3 block http://bl.ocks.org/lakenen/8529857 which is a rendering of a candlestick-ish chart. Its output looks like this:
The data for each block is 1 day worth of financial stock numbers: the high, low, start, and close.
Typically, candlestick charts are different, though. Typically, the blocks are evenly spaced, and there is one gridline per day, and the x axis is labeled once per day. Here's an example of this on google finance:
And here's the code that renders the d3 chart from above:
var margin = 50;
var chart = d3.select("#chart")
.append("svg:svg")
.attr("class", "chart")
.attr("width", width)
.attr("height", height);
var y = d3.scale.linear()
.domain([d3.min(data.map(function(x) {return x["Low"];})), d3.max(data.map(function(x){return x["High"];}))])
.range([height-margin, margin]);
var x = d3.scale.linear()
.domain([d3.min(data.map(function(d){return d.timestamp;})), d3.max(data.map(function(d){ return d.timestamp;}))])
.range([margin,width-margin]);
chart.selectAll("line.x")
.data(x.ticks(10))
.enter().append("svg:line")
.attr("class", "x")
.attr("x1", x)
.attr("x2", x)
.attr("y1", margin)
.attr("y2", height - margin)
.attr("stroke", "#ccc");
chart.selectAll("line.y")
.data(y.ticks(10))
.enter().append("svg:line")
.attr("class", "y")
.attr("x1", margin)
.attr("x2", width - margin)
.attr("y1", y)
.attr("y2", y)
.attr("stroke", "#ccc");
chart.selectAll("text.xrule")
.data(x.ticks(10))
.enter().append("svg:text")
.attr("class", "xrule")
.attr("x", x)
.attr("y", height - margin)
.attr("dy", 20)
.attr("text-anchor", "middle")
.text(function(d){ var date = new Date(d * 1000); return (date.getMonth() + 1)+"/"+date.getDate(); });
chart.selectAll("text.yrule")
.data(y.ticks(10))
.enter().append("svg:text")
.attr("class", "yrule")
.attr("x", width - margin)
.attr("y", y)
.attr("dy", 0)
.attr("dx", 20)
.attr("text-anchor", "middle")
.text(String);
chart.selectAll("rect")
.data(data)
.enter().append("svg:rect")
.attr("x", function(d) { return x(d.timestamp); })
.attr("y", function(d) {return y(max(d.Open, d.Close));})
.attr("height", function(d) { return y(min(d.Open, d.Close))-y(max(d.Open, d.Close));})
.attr("width", function(d) { return 0.5 * (width - 2*margin)/data.length; })
.attr("fill",function(d) { return d.Open > d.Close ? "red" : "green" ;});
chart.selectAll("line.stem")
.data(data)
.enter().append("svg:line")
.attr("class", "stem")
.attr("x1", function(d) { return x(d.timestamp) + 0.25 * (width - 2 * margin)/ data.length;})
.attr("x2", function(d) { return x(d.timestamp) + 0.25 * (width - 2 * margin)/ data.length;})
.attr("y1", function(d) { return y(d.High);})
.attr("y2", function(d) { return y(d.Low); })
.attr("stroke", function(d){ return d.Open > d.Close ? "red" : "green"; })
}
I've tried tinkering with the .data(x.ticks(10)) values, which changes the number of ticks, but I'm not sure how to set that equal to the value of datapoints, and I'm also unsure of how exactly the d3.scale.linear().domain(...) stuff is changing the data before rendering begins.
So, how do I made the blocks evenly spaced so that I can make a gridline per block and a label per block?
The problem is that the graph you are trying to emulate doesn't have a linear x-axis based on time (it's missing days). You'll need to use a linear scale based on the number of data points and then custom set the label values.
I didn't really test this code so there may be bugs. However, this is how I would approach the problem.
// Create a formatter that given an index, will print the day number for the
// data at that index in data
var dayFormatter = d3.time.format('%d');
var dayAxisFormatter = function(d) {
return dayFormatter(new Date(data[d].timestamp));
}
// Create a formatter that given an index, will print the short month name
// along with the day number for the data at that index in data
var dayWithMonthFormatter = d3.time.format('%b %d');
var dayWithMonthAxisFormatter = function(d) {
return dayWithMonthFormatter(new Date(data[d].timestamp));
}
// Custom formatter to handle printing just the day number except for the first
// instance of the month, there we will print the short month and the day
// helper to create the formatter function that d3 accepts
function timeFormat(formats) {
return function(date) {
var i = formats.length - 1, f = formats[i];
while (!f[1](date)) f = formats[--i];
return f[0](date);
};
}
var firstDone = {}; // track the months so first instance gets month label
var tickFormatter = timeFormat([
[dayAxisFormatter, function(d) { return true; }],
[dayWithMonthFormatter, function(d) {
var month = (new Date(data[d].timestamp)).getMonth();
var result = !firstDone['m' + month];
firstDone['m' + month] = true;
return result;
}],
]);
// Set up a regular linear scale. This would normally just count up from
// 0 to d.length, but we'll use a custom formatter to print out our day
// numbers instead.
var x = d3.scale.linear()
.domain([0, d.length]) // set the domain to be from 0 to # of points
.range([margin,width-margin]);
// Set up the axis to use our customer formatter
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(height)
.tickFormat(tickFormatter);
// Now when you go to draw your data, you need to remember that the
// underlying scale is based on the data index, not the data timestamp.
chart.selectAll("rect")
.data(data)
.enter().append("svg:rect")
.attr("x", function(d, i) { return x(i); })
...

Resources