display rectangles based on values - d3.js

I am trying to draw rectangle size based on the values. I want to draw bigger rectangle if the value is more and relatively smaller size if the value is lesser.
My code:
var rect = svg.selectAll("rect")
.data(filtereddata)
.enter().append("rect")
.attr("id", function(d,i) { console.log(d); return d.instancegroup; })
.attr("rx",3)
.attr("ry",3)
.style("fill", function(d,i) { return "#01DF01";})
.attr("x", function(d,i) { return i * 80; })
.attr("width",function (d,i){
if (d.value < 80)
{
return 40;
}
else
{
return d.value/4;
}
})
.on("click",function(d,i) { console.log(d);})
.attr("y", function(d) { return 40; })
.attr("height", function(d) {
if (d.value < 80)
{
return 15;
}
else
{
return d.value/5;
}
})
However the spacing between the rectangles does not work properly. Is there a better way to resolve this.
I tried with axis too but didnt work. I was getting wrong to align the x-axis.
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);
var y = d3.scale.linear().range([height, 0]);
var rect = svg.selectAll("rect")
.data(filtereddata)
.enter().append("rect")
.attr("id", function(d,i) { console.log(d); return d.instancegroup; })
.attr("rx",3)
.attr("ry",3)
.style("fill", function(d,i) { return "#01DF01";})
.attr("x", function (d,i) {
if(i ==0 )
{
return 40;
}
else
{
return x(d.value) + 100 ;
}
})
.attr("y", function (d,i) { return 40;})
.attr("width", function (d,i) { return height - y(d.value);})
.attr("height", function (d,i) { console.log(x(d.value)); return height - y(d.value);})
The data :
[{"status: "Up",value: "300"},{"status: "Up",value: "200"},{"status: "Up",value: "35"}]

Since x positions are accumulated using widths of previous rectangles having varying dimensions, I'd pre-calculate these values and attach them to the objects prior to binding the data array via d3:
var SPACING = 20;
var data = [
{"status": "Up", value: "300"},
{"status": "Up", value: "200"},
{"status": "Up", value: "35"}
];
data.forEach(function(d, i, array) {
d.width = d.value < 80 ? 40 : d.value / 4;
d.height = d.value < 80 ? 15 : d.value / 5;
d.x = i == 0 ? 0 : array[i-1].x + array[i-1].width + SPACING;
d.y = 40;
});
Later on you are able to directly access these properties without the need for further calculations:
var rect = svg.selectAll("rect")
.data(filtereddata)
.enter().append("rect")
.attr({
"id": function (d, i) { return d.instancegroup; },
"rx": 3,
"ry": 3,
"x": function (d) { return d.x; },
"y": function (d) { return d.y; },
"width": function (d) { return d.width; },
"height": function (d) { return d.height; }
})
.on("click", function (d) {
console.log(d);
})
.style("fill", function (d) {
return "#01DF01";
})

Related

Add moving circles to transitioning nested paths in d3.js

I want to add circles to an svg for each path created by the nest so that the circles lead the drawing of the paths. Similar to this fiddle, but with nested data.
Here is some reproducible example code in d3.v4 and a runnable snippet of the paths I would like to add circles to:
var data=[{group:1,x:6,y:8},
{group:1,x:4,y:4},
{group:1,x:1,y:2},
{group:2,x:8,y:3},
{group:2,x:1,y:6},
{group:2,x:7,y:5},
{group:3,x:7,y:1},
{group:3,x:6,y:6},
{group:3,x:3,y:2}];
var height = 600
var width = 800
var svg = d3.select("body")
.append("svg")
.attr("height", "100%")
.attr("width", "100%");
var colours = ["#0000FF",
"#FF0000",
"#00FF00"
];
var line = d3.line()
.x(function(d, i) {
return d.x * 20;
})
.y(function(d, i) {
return d.y * 20;
})
.curve(d3.curveNatural);
function tweenDash() {
var l = this.getTotalLength(),
i = d3.interpolateString("0," + l, l + "," + l);
return function(t) {
return i(t);
};
}
function transition(selection) {
selection.each(function() {
d3.select(this).transition()
.duration(5000)
.attrTween("stroke-dasharray", tweenDash)
.ease(d3.easeLinear);
})
}
var dataGroup = d3.nest()
.key(function(d) {
return d.group;
})
.entries(data);
dataGroup.forEach(function(d, i) {
var path = svg.append("path")
.attr("d", line(d.values))
.attr("stroke", colours[i])
.attr("stroke-width", 1)
.attr("fill", "none");
transition(d3.selectAll("path"))
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
Using this example, I've added circles to your paths. Note that they do not follow the path perfectly, but this is covered by increasing their radius.
A couple of times, you use .forEach() or .each() to iterate over the data or the selection, but you don't need to. d3 is made to work with arrays of data at once, so you can easily apply one transformation to multiple elements, each using their own data. Getting used to that can drastically improve your developer experience.
var data = [{
group: 1,
x: 6,
y: 8
},
{
group: 1,
x: 4,
y: 4
},
{
group: 1,
x: 1,
y: 2
},
{
group: 2,
x: 8,
y: 3
},
{
group: 2,
x: 1,
y: 6
},
{
group: 2,
x: 7,
y: 5
},
{
group: 3,
x: 7,
y: 1
},
{
group: 3,
x: 6,
y: 6
},
{
group: 3,
x: 3,
y: 2
}
];
var height = 600
var width = 800
var svg = d3.select("body")
.append("svg")
.attr("height", "100%")
.attr("width", "100%");
var colours = ["#0000FF",
"#FF0000",
"#00FF00"
];
var line = d3.line()
.x(function(d, i) {
return d.x * 20;
})
.y(function(d, i) {
return d.y * 20;
})
.curve(d3.curveNatural);
function tweenDash() {
var l = this.getTotalLength(),
i = d3.interpolateString("0," + l, l + "," + l);
return function(t) {
return i(t);
};
}
function tweenCircle(i, paths) {
var path = paths
.filter(function(_, j) { return i === j; })
.node();
var l = path.getTotalLength();
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + [p.x, p.y] + ")";
};
}
function transition(path, circle) {
path.transition()
.duration(2000)
.attrTween("stroke-dasharray", tweenDash)
.ease(d3.easeLinear);
circle.transition()
.duration(2000)
.attrTween("transform", function(d, i) { return tweenCircle(i, path); })
.ease(d3.easeLinear);
}
var dataGroup = d3.nest()
.key(function(d) {
return d.group;
})
.entries(data);
var path = svg.selectAll('path')
.data(dataGroup)
.enter()
.append("path")
.attr("d", function(d) { return line(d.values); })
.attr("stroke", function(d, i) { return colours[i]; })
.attr("stroke-width", 1)
.attr("fill", "none");
var circle = svg.selectAll('circle')
.data(dataGroup)
.enter()
.append("circle")
.attr("fill", function(d, i) { return colours[i]; })
.attr("transform", function(d) {
const start = d.values[0];
return "translate(" + [start.x, start.y] + ")"; })
.attr("r", 8);
transition(path, circle);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

How to Drag line by its end points using rectangle as end points error?

I am new to d3.js and please forgive me if this sounds like a naive question. I have plotted a line (d3 v4) which can be draggable by its end points. The end points are rectangle.
The current output looks as below :
This is how it looks
The challenge that i am facing is - when i start dragging the point, the line seems to take its origin from the top left corner. When i drag the second point of the same line, the line drags / moves as expected.
The sample data looks as below :
The sample data
Requesting your suggestions / inputs on how to fix the above issue.
Below is the attached code that i am using :
var margin = { top: 0, right: 0, bottom: 0, left: 0 },
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
// Creating the colour Category
var color = d3.scaleOrdinal(d3.schemeCategory10);
var y = d3.scaleLinear().range([390, 0]);
// Scale the range of the data
y.domain([0, d3.max(data, function (d) { return Math.max(d.nonpromotedprice, d.promotedprice)*1.2; })]).nice();
// Line for the 1st Block
var lines = svg.selectAll("line")
.data(data)
.enter()
.append('line')// attach a line
.style("stroke", "#E6EAEE")
.style("stroke-width", 8) // colour the line
.attr("x1", function (d) { return d.x_value; }) // x position of the first end of the line
.attr("y1", function (d) { return y(d.nonpromotedprice); }) // y position of the first end of the line
.attr("x2", function (d) { return d.x_value; }) // x position of the second end of the line
.attr("y2", function (d) { return y(d.promotedprice); });
// Add the Y Axis
svg.append("g")
.attr("class", "grid")
.attr("fill", "lightgrey")
.attr("stroke-width", 0.7)
.attr("stroke-opacity", 0.2)
.call(d3.axisLeft(y)
.tickSize(-400)
.tickFormat(""));
var topEndPoints = data.map(function (line, i) {
return {
'x': line.x_value,
'y': line.nonpromotedprice,
'marker': 'marker-start',
'lineIndex': i
};
});
var bottomEndPoints = data.map(function (line, i) {
return {
'x': line.x_value,
'y': line.promotedprice,
'marker': 'marker-end',
'lineIndex': i
};
});
var MiddleEndPoints = data.map(function (line, i) {
return {
'x': line.x_value,
'y': line.avgprice,
'marker': 'marker-middle',
'lineIndex': i
};
});
var endPointsData = topEndPoints.concat(bottomEndPoints, MiddleEndPoints);
// Pointer to d3 rectangles
var endPoints = svg
.selectAll('rect')
.data(endPointsData)
.enter()
.append('rect')
.attr("width", 12)
.attr("height", 8)
.attr("x", function (d) { return d.x - 6; })
.attr("y", function (d) { return y(d.y); })
//.attr("cx", function (d) { return d.x; })
//.attr("cy", function (d) { return d.y; })
//.attr('r',7)
.attr("fill", function (d) { return color(d.x); })
.call(d3.drag()
//.origin(function(d) { return y(d.y); })
.subject(function() {
var t = d3.select(this);
return {x: t.attr("x"), y: t.attr("y")};
})
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// draw the logo
svg.selectAll("image")
.data(data)
.enter()
.append("svg:image")
.attr("xlink:href", function (d) { return d.logo; })
//.append("rect")
.attr("x", function (d) { return d.x_value - 13; })
.attr("y", function (d) { return y(d.nonpromotedprice + 35); })
.attr("height", 25)
.attr("width", 25);
function dragstarted() {
d3.select(this).classed("active", true).attr('y', d.y = y(d3.event.y));
}
function dragged(d, i) {
var marker = d3.select(this);
// Update the marker properties
marker
//.attr('cx', d.x = d3.event.x)
.attr('y', d.y = d3.event.y);
// Update the line properties
lines
.filter(function (lineData, lineIndex) {
return lineIndex === d.lineIndex;
})
.attr('x1', function (lineData) {
return d.marker === 'marker-start' ? lineData.x1 = d.x : lineData.x1;
})
.attr('y1', function (lineData) {
return d.marker === 'marker-start' ? lineData.y1 = d.y : lineData.y1;
})
.attr('x2', function (lineData) {
return d.marker === 'marker-end' ? lineData.x2 = d.x : lineData.x2;
})
.attr('y2', function (lineData) {
return d.marker === 'marker-end' ? lineData.y2 = d.y : lineData.y2;
});
}
function dragended() {
d3.select(this).classed("active", false);
Shiny.setInputValue("pricechanged",
{price: (d3.max(data, function (d) { return Math.max(d.nonpromotedprice, d.promotedprice); }) -(d3.event.y / 390)* d3.max(data, function (d) { return Math.max(d.nonpromotedprice, d.promotedprice); }))*1.19},
{priority: "event"}
);
}

How can I refactor a d3 pie to accept more or less data points?

I have a project that almost works the way I want. When a smaller dataset is added, slices are removed. It fails when a larger dataset is added. The space for the arc is added but no label or color is added for it.
This is my enter() code:
newArcs.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
What am I doing wrong?
I've fixed the code such that it works now:
// Tween Function
var arcTween = function(a) {
var i = d3.interpolate(this.current || {}, a);
this.current = i(0);
return function(t) {
return arc(i(t));
};
};
// Setup all the constants
var duration = 500;
var width = 500
var height = 300
var radius = Math.floor(Math.min(width / 2, height / 2) * 0.9);
var colors = ["#d62728", "#ff9900", "#004963", "#3497D3"];
// Test Data
var d2 = [{
label: 'apples',
value: 20
}, {
label: 'oranges',
value: 50
}, {
label: 'pears',
value: 100
}];
var d1 = [{
label: 'apples',
value: 100
}, {
label: 'oranges',
value: 20
}, {
label: 'pears',
value: 20
}, {
label: 'grapes',
value: 20
}];
// Set the initial data
var data = d1
var updateChart = function(dataset) {
arcs = arcs.data(donut(dataset), function(d) { return d.data.label });
arcs.exit().remove();
arcs.enter()
.append("path")
.attr("stroke", "white")
.attr("stroke-width", 0.8)
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
arcs.transition()
.duration(duration)
.attrTween("d", arcTween);
sliceLabel = sliceLabel.data(donut(dataset), function(d) { return d.data.label });
sliceLabel.exit().remove();
sliceLabel.enter()
.append("text")
.attr("class", "arcLabel")
.attr("transform", function(d) {
return "translate(" + (arc.centroid(d)) + ")";
})
.attr("text-anchor", "middle")
.style("fill-opacity", function(d) {
if (d.value === 0) {
return 1e-6;
} else {
return 1;
}
})
.text(function(d) {
return d.data.label;
});
sliceLabel.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + (arc.centroid(d)) + ")";
})
.style("fill-opacity", function(d) {
if (d.value === 0) {
return 1e-6;
} else {
return 1;
}
});
};
var color = d3.scale.category20();
var donut = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.innerRadius(radius * .4)
.outerRadius(radius);
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var arc_grp = svg.append("g")
.attr("class", "arcGrp")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var label_group = svg.append("g")
.attr("class", "lblGroup")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")");
var arcs = arc_grp.selectAll("path");
var sliceLabel = label_group.selectAll("text");
updateChart(data);
// returns random integer between min and max number
function getRand() {
var min = 1,
max = 2;
var res = Math.floor(Math.random() * (max - min + 1) + min);
//console.log(res);
return res;
}
// Update the data
setInterval(function(model) {
var r = getRand();
return updateChart(r == 1 ? d1 : d2);
}, 2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Incomplete line chart with missing points and missing lines

var maindata=[
{"date":"21-APR-16 04:19 AM","Delhi":30,"Mumbai":28},
{"date":"21-APR-16 05:19 AM","Delhi":32,"Mumbai":30},
{"date":"21-APR-16 06:19 AM","Delhi":34,"Mumbai":34},
{"date":"21-APR-16 10:19 AM","Kolkata":34},
{"date":"21-APR-16 11:19 AM","Delhi":48,"Chennai":40,"Kolkata":36}
];
var that = this;
var deepClonedCopy = jQuery.extend(true, {}, maindata);
var data;
data = $.map(deepClonedCopy, function(el) { return el });
// var passedheight = this.getHeight();
//var containerWidth = jQuery.sap.byId(this.oParent.sId).width() || 800; // gets super parent width
var margin = {top: 15, right: 30, bottom: 20, left: 30},
width = 960- margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
if(data.length >= 1){
//alert(JSON.stringify(data));
var parseDate = d3.time.format("%d-%b-%y %H:%M %p").parse;
var maxDate = d3.time.hour.offset(parseDate(d3.max(data, function(d) { return d.date; })),+1);
var minDate = d3.time.hour.offset(parseDate(d3.min(data, function(d) { return d.date; })),-1);
var div = d3.select("#toolTip");
var x = d3.time.scale()
.domain([minDate, maxDate])
.range([0, width]);
var y = d3.scale.linear()
.range([Math.ceil(height), 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom").ticks(4);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left").ticks(4).tickSize(-width, 0, 0);
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.tonneValue); });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date" && key !== "maxLength"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var wsfs = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
})
};
});
//x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
//d3.min(wsfs, function(c) { return d3.min(c.values, function(v) { return v.tonneValue; }); }),
0,
Math.ceil(d3.max(wsfs, function(c) { return d3.max(c.values, function(v) { return v.tonneValue; }); }))
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var wsf = svg.selectAll(".wsf")
.data(wsfs)
.enter().append("g")
.attr("class", "wsf");
wsf.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
wsf.selectAll("dot")
.data(function(d) { return d.values;})
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.tonneValue); })
.attr("stroke", function (d) {
return color(this.parentNode.__data__.name) })
.attr("fill", function (d) {
return color(this.parentNode.__data__.name) })
//.attr("fill-opacity", .5)
//.attr("stroke-width", 2)
.on("mouseover", function (d) {
formatDate = d3.time.format("%d-%m-%Y %H:%M %p");
div.transition().duration(100).style("opacity", .9);
div.html(/*this.parentNode.__data__.name + "<br/>" +*/ d.tonneValue /*+ "<br/>" + "<br/>"*/ +" Tonne handled at "+ formatDate(d.date))
.style("left", (d3.event.pageX) + "px").style("top", (d3.event.pageY - 28) + "px").attr('r', 8);
d3.select(this).attr('r', 8)
}).on("mouseout", function (d) {
div.transition().duration(600).style("opacity", 0)
d3.select(this).attr('r', 3);
});
var legendNames = d3.keys(data[0]).filter(function(key) { return key !== "date" });
data.forEach(function(d) {
d.ages = legendNames.map(function(name) { return {name: name, value: +d[name]}; });
});
var legend = svg.selectAll(".legend")
.data(legendNames.slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 4)
.style("fill", function(d) {return color(d); });
legend.append("text")
.attr("x", width - 24)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
}
The above code gives 2 lines, until first 3 json elements till the data format is similar.
but from 4th element there is no Delhi / Mumbai and there is just Kolkata, but I don't find a line for Kolkata 34, Kolkata 36 neither Delhi's 48 point, where Delhi's line should extend from 34 to 48 and Chennai a simple dot is expected as there is only one point of Chennai i.e. 40.
Can any one tell where am I doing wrong. Here is a fiddle.
The main problem in the code (missing lines and points and incomplete legend) is that your data needs some structuring.
You dataset some times has cities which may or may not be present.
So The first task will be to make a proper data set
From this:
var maindata = [{
"date": "21-APR-16 04:19 AM",
"Delhi": 30,
"Mumbai": 28
}, {
"date": "21-APR-16 05:19 AM",
"Delhi": 32,
"Mumbai": 30
}, {
"date": "21-APR-16 06:19 AM",
"Delhi": 34,
"Mumbai": 34
}, {
"date": "21-APR-16 10:19 AM",
"Kolkata": 34
}, {
"date": "21-APR-16 11:19 AM",
"Delhi": 48,
"Chennai": 40,
"Kolkata": 36
}];
To this:
[
{
"name":"Delhi",
"values":[
{
"date":"2016-04-20T22:49:00.000Z",
"value":30
},
{
"date":"2016-04-20T23:49:00.000Z",
"value":32
},
{
"date":"2016-04-21T00:49:00.000Z",
"value":34
},
{
"date":"2016-04-21T05:49:00.000Z",
"value":48
}
]
},
{
"name":"Mumbai",
"values":[
{
"date":"2016-04-20T22:49:00.000Z",
"value":28
},
{
"date":"2016-04-20T23:49:00.000Z",
"value":30
},
{
"date":"2016-04-21T00:49:00.000Z",
"value":34
}
]
},
{
"name":"Kolkata",
"values":[
{
"date":"2016-04-21T04:49:00.000Z",
"value":34
},
{
"date":"2016-04-21T05:49:00.000Z",
"value":36
}
]
},
{
"name":"Chennai",
"values":[
{
"date":"2016-04-21T05:49:00.000Z",
"value":40
}
]
}
]
So now all cities have their respective data.
Number of cities x so number of lines will be x.
To make the dataset do this:
var cities = []
var parseDate = d3.time.format("%d-%b-%y %H:%M %p").parse;
maindata.forEach(function(d) {
cities.push(d3.keys(d).filter(function(key) {
return key !== "date" && key !== "maxLength";
}));
});
cities = d3.set(d3.merge(cities)).values();//get all cites and make it unique
console.log(cities)
var myData = [];
var allValues = [];
var allDates =[];
//generate cities and its values as shown in my dataset above.
cities.forEach(function(city){
var cityData = {};
cityData.name = city;
cityData.values = [];
maindata.forEach(function(md){
if (md[city]){
allValues.push(md[city])
allDates.push(parseDate(md.date))
cityData.values.push({date: parseDate(md.date), value: md[city]})
}
})
myData.push(cityData)
});
Next make lines like this:
var wsf = svg.selectAll(".wsf")
.data(myData)
.enter().append("g")
.attr("class", "wsf");
wsf.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.values);
})
.style("stroke", function(d) {
return color(d.name);
});
And Legends like this:
var legend = svg.selectAll(".legend")
.data(cities)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 4)
.style("fill", function(d) {
return color(d);
});
legend.append("text")
.attr("x", width - 24)
.attr("y", 6)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
}
working code here
Here is the updated fiddle
some keys(i.e. Kolkata)aren't included in binding data
Original code:
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date" && key !== "maxLength"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var wsfs = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
})
};
});
To(you need change ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'] to code more formally):
var wsfs = ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'].map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
}).filter(function(d) {
return !isNaN(d.tonneValue)
})
};
});
Filter data
Original code:
var wsfs = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
})
};
});
To:
var wsfs = ['Delhi', 'Mumbai', 'Kolkata', 'Chennai'].map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, tonneValue: +d[name]};
}).filter(function(d) {
return !isNaN(d.tonneValue)
})};
});

Aligning a stacked barchart to the bottom of the chart

Thanks in advance for any help.
I'm new to D3 and javascript as a whole. I've been pretty stuck on this for a while now even searching through other similar posts.
I'd like to flip my stacked bar chart appropriately so that it aligns to the bottom of the SVG.
When I do try it the way I think it should be done, I get a "invalid negative value for '' message.
var dataset = [
[
{ x: 0, y: 5 },
{ x: 1, y: 4 },
{ x: 2, y: 2 },
{ x: 3, y: 7 },
{ x: 4, y: 23 }
],
[
{ x: 0, y: 10 },
{ x: 1, y: 12 },
{ x: 2, y: 19 },
{ x: 3, y: 23 },
{ x: 4, y: 17 }
],
[
{ x: 0, y: 22 },
{ x: 1, y: 28 },
{ x: 2, y: 32 },
{ x: 3, y: 35 },
{ x: 4, y: 43 }
]
];
//Width and Height
var w = 500;
h = 300;
//Create SVG canvas
var svg = d3.select('body').append('svg')
.attr('width', w)
.attr('height', h);
//Set up Stack
var stack = d3.layout.stack();
//Stack dataset
stack(dataset);
//Create scales
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset[0].length))
.rangeRoundBands([0,w], 0.05);
var yScale = d3.scale.linear()
.domain([0,
d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})
])
.range([0, h]);
//Create colors for scale
var colors = d3.scale.category10();
//Create a Group for each row of data
var groups = svg.selectAll('g')
.data(dataset)
.enter() //only creates placeholder
.append('g') //creates group
.style('fill', function(d, i) {
return colors(i);
});
//Add a rectangle for each datavalue
var rects = groups.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) { return yScale(d.y0 + d.y); })
.attr("height", function(d) { return yScale(d.y0) - yScale(d.y0 + d.y); })
.attr("width", xScale.rangeBand());
I think you need to change the way height is getting calculated for each bar. See this plnkr. Is this something you were looking for. May be you can use height (h) variable for your calculations. I changed y parameters like below.
//Add a rectangle for each datavalue
var rects = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
/* .attr("y", function(d) { return yScale(d.y0 + d.y); })
.attr("height", function(d) { return yScale(d.y0) - yScale(d.y0 + d.y); })
.attr("y", function(d) { return yScale(d.y0 ); })
.attr("height", function(d) { return yScale(d.y0 + d.y);})
*/
.attr("y", function(d) {
return yScale(d.y0 + d.y);
})
.attr("height", function(d) {
return h - yScale(d.y0 + d.y);
})
.attr("width", xScale.rangeBand());

Resources