D3 multi line chart mouseover issue - javascript
I found a reference to the issue I am facing in this link
'nemesv' has provided with a solution to the actual issue.
var myApp = angular.module('app', []);
myApp.directive("lineChart", function() {
return {
restrict: 'E',
scope: {
data: '=',
id: '#'
},
link: function (scope, element, attrs) {
scope.$watch( 'data', function ( data ) {
d3.select("#"+attrs.id).select("svg").remove();
if (data) {
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = element[ 0 ].parentElement.offsetWidth - margin.left - margin.right,
height = element[ 0 ].parentElement.offsetHeight - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").parse;
var bisectDate = d3.bisector(function(d) { return d[0]; }).left;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.innerTickSize(-height)
.ticks(4)
.outerTickSize(0)
.tickPadding(5)
.tickFormat(function(d) { return d3.time.format('%d/%m %H:%M')(new Date(d)); });
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
var line = d3.svg.line()
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[1]); });
var svg = d3.select(element[0]).append("svg")
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox','0 0 '+ element[ 0 ].parentElement.offsetWidth +' '+ element[ 0 ].parentElement.offsetHeight )
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var minX = d3.min(data, function (item) { return d3.min(item.values, function (d) { return d[0]; }); });
var maxX = d3.max(data, function (item) { return d3.max(item.values, function (d) { return d[0]; }); });
var minY = d3.min(data, function (item) { return d3.min(item.values, function (d) { return d[1]; }); });
var maxY = d3.max(data, function (item) { return d3.max(item.values, function (d) { return d[1]; }); });
x.domain([minX, maxX]);
y.domain([0, maxY]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var domaine = svg.selectAll(".domaine")
.data(data)
.enter().append("g")
.attr("class", "domaine");
domaine.append("path")
.attr("class", "line")
.attr("d", function (d) {
return line(d.values);
})
.style("stroke", function (d) {
return d.color;
});
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");
for(var i=0;i<data.length;i++){
focus.append("g")
.attr("class", "focus"+i)
.append("circle")
.attr("r", 4.5);
svg.select(".focus"+i)
.append("text")
.attr("x", 9)
.attr("dy", ".35em");
}
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]);
var series = data.map(function(e) {
var i = bisectDate(e.values, x0, 1),
d0 = e.values[i - 1],
d1 = e.values[i];
return x0 - d0[0] > d1[0] - x0 ? d1 : d0;
});
for(var i=0; i<series.length;i++){
var selectedFocus = svg.selectAll(".focus"+i);
selectedFocus.attr("transform", "translate(" + x(series[i][0]) + "," + y(series[i][1]) + ")");
selectedFocus.select("text").text(series[i][1]);
}
}
}
});
}
};
});
function MainCtrl($scope) {
$scope.lineData = [{"key": "users","color": "#16a085","values": [[1413814800000,4034.418],[1413815400000,5604.155000000001],[1413816000000,6343.079],[1413816600000,7308.226],[1413817200000,9841.185],[1413817800000,6571.891],[1413818400000,4660.6005000000005],[1413819000000,4555.4795],[1413819600000,5963.723],[1413820200000,9179.9595]]},{"key": "users 2","color": "#d95600","values": [[1413814800000,3168.183],[1413815400000,1530.8435],[1413816000000,2416.071],[1413816600000,1274.309],[1413817200000,1105.0445],[1413817800000,2086.0299999999997],[1413818400000,712.642],[1413819000000,1676.725],[1413819600000,3721.46],[1413820200000,2887.7975]]}];
}
Presently, if I hover over one line, I can see another dot hovering over the other line as well - i.e. mouseover is working simultaneously on both the lines. Could this mouseover/tooltip be done separately on each line i.e. when I hover over one line, focus circle will appear for only that line?
Related
D3Js Display Multi-line chart
I have the following data structure as on screenshot. Could someone help me to display a multiline chart with it. I am stuck at setting the range and domain. As I am not able to find the minimum and maximum values. Looping at object, might be bad Idea, as there potentially going to be a lot of data. Could someone suggest the correct way to do this? Image with data structure I am trying to use this as an example, and it has a similar task. But due to some circumstances I can not send data through AJAX in the same format as it is in this example. http://jsfiddle.net/JYS8n/1/ var margin = { top: 20, right: 80, bottom: 30, left: 50 }, width = 360 - margin.left - margin.right, height = 300 - margin.top - margin.bottom; var parseDate = d3.time.format("%Y%m%d").parse; var x = d3.time.scale() .range([0, width]); var y = d3.scale.linear() .range([height, 0]); var color = d3.scale.category10(); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var line = d3.svg.line() .interpolate("basis") .x(function (d) { return x(d.Date); }) .y(function (d) { return y(d.Value); }); 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(data.map(function (d) { return d.City; })); data.forEach(function (kv) { kv.Data.forEach(function (d) { d.Date = parseDate(d.Date); }); }); var cities = data; var minX = d3.min(data, function (kv) { return d3.min(kv.Data, function (d) { return d.Date; }) }); var maxX = d3.max(data, function (kv) { return d3.max(kv.Data, function (d) { return d.Date; }) }); var minY = d3.min(data, function (kv) { return d3.min(kv.Data, function (d) { return d.Value; }) }); var maxY = d3.max(data, function (kv) { return d3.max(kv.Data, function (d) { return d.Value; }) }); x.domain([minX, maxX]); y.domain([minY, maxY]); 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", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("Temperature (ºF)"); var city = svg.selectAll(".city") .data(cities) .enter().append("g") .attr("class", "city"); city.append("path") .attr("class", "line") .attr("d", function (d) { return line(d.Data); }) .style("stroke", function (d) { return color(d.City); }); city.append("text") .datum(function (d) { return { name: d.City, date: d.Data[d.Data.length - 1].Date, value: d.Data[d.Data.length - 1].Value }; }) .attr("transform", function (d) { return "translate(" + x(d.date) + "," + y(d.value) + ")"; }) .attr("x", 3) .attr("dy", ".35em") .text(function (d) { return d.name; });
how to make the x axis scrollable in a d3 line chart graph
Below is the code I used. This code is working fine to me, but problem is when the out put comes for the below code, some part of the graph doesn't show in x axis. Some parts are hidden because of the x axis length is not enough to show. So I decided to make this graph scrollable and also I tried a code to make this scrollable.But It didnt work. please help me to sort out this. I used this reference :- http://computationallyendowed.com/blog/2013/01/21/bounded-panning-in-d3.html graph : HTML CODE: <div class="row"> <div class="col-sm-12"> <div class="lineChart1" style=" overflow: scroll"> <svg width="960" height="500" style=" overflow: scroll"></svg> </div> </div> </div> JAVASCRIPT CODE: function createLineChart() { var number=1; var data = [ { label: "Execution 1 - buddhika#gmail.com", x: ["1","2","2","3","3","4","4","5","5","6","6","7","7","8","8","9","9","10","10","11","11","12","12"], y: ["3","3","3","3","3","3","2","2","3","3","3","3","3","3","3","3","3","3","2","2","3","3","3","3"] }] ; var xy_chart = d3_xy_chart() .width(960) .height(500) .xlabel("TCS") .ylabel("STATUS"); var svg = d3.select(".lineChart" + number).append("svg") .datum(data) .call(xy_chart); function d3_xy_chart() { var width = 640, height = 480, xlabel = "X Axis Label", ylabel = "Y Axis Label"; function chart(selection, svg) { selection.each(function (datasets) { // // Create the plot. // var margin = {top: 20, right: 80, bottom: 30, left: 50}, innerwidth = width - margin.left - margin.right, innerheight = height - margin.top - margin.bottom; var x_scale = d3.scale.linear() .range([0, innerwidth]) .domain([d3.min(datasets, function (d) { return d3.min(d.x); }), d3.max(datasets, function (d) { return d3.max(d.x); })]); var y_scale = d3.scale.linear() .range([innerheight, 0]) .domain([d3.min(datasets, function (d) { return d3.min(d.y); }), d3.max(datasets, function (d) { return d3.max(d.y); })]); var color_scale = d3.scale.category10() .domain(d3.range(datasets.length)); var x_axis = d3.svg.axis() .scale(x_scale) .orient("bottom") .tickFormat(function (d, i) { if (d % 1 == 0) { return parseInt(d) } else { return " " } }); var y_axis = d3.svg.axis() .scale(y_scale) .orient("left") .tickFormat(function (d, i) { if (d == "1") { return "NOT EXECUTED" } else if (d == "2") { return "FAILED" } else if (d == "3") { return "PASSED" } else { return " " } }); var x_grid = d3.svg.axis() .scale(x_scale) .orient("bottom") .tickSize(-innerheight) .tickFormat(""); var y_grid = d3.svg.axis() .scale(y_scale) .orient("left") .tickSize(-innerwidth) .tickFormat(""); var draw_line = d3.svg.line() .interpolate("linear") .x(function (d) { return x_scale(d[0]); }) .y(function (d) { return y_scale(d[1]); }); var svg = d3.select(this) .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); svg.append("g") .attr("class", "x grid") .attr("transform", "translate(0," + innerheight + ")") .call(x_grid); svg.append("g") .attr("class", "y grid") .call(y_grid); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + innerheight + ")") .call(x_axis) .append("text") .attr("dy", "-.71em") .attr("x", innerwidth) .style("text-anchor", "end") .text(xlabel); svg.append("g") .attr("class", "y axis") .call(y_axis) .append("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", "0.71em") .style("text-anchor", "end") .text(ylabel); var data_lines = svg.selectAll(".d3_xy_chart_line") .data(datasets.map(function (d) { return d3.zip(d.x, d.y); })) .enter().append("g") .attr("class", "d3_xy_chart_line"); data_lines.append("path") .attr("class", "line") .attr("d", function (d) { return draw_line(d); }) .attr("stroke", function (_, i) { return color_scale(i); }); data_lines.append("text") .datum(function (d, i) { return {name: datasets[i].label, final: d[d.length - 1]}; }) .attr("transform", function (d) { return ( "translate(" + x_scale(d.final[0]) + "," + y_scale(d.final[1]) + ")" ); }) .attr("x", 3) .attr("dy", ".35em") .attr("fill", function (_, i) { return color_scale(i); }) .text(function (d) { return d.name; }); // scrolling code START var xscale = d3.scale.linear().domain([0, 12]).range([0, 12]), yscale = d3.scale.linear().domain([0, 100]).range([innerheight, 0]); var line = d3.svg.line() .x(function(d) { return xscale(d[0]); }) .y(function(d) { return yscale(d[1]); }) .interpolate('basis'); svg.append('g') .datum(datasets) .append('path') .attr('class', 'data') .attr('d', line); var zoom = d3.behavior.zoom() .scaleExtent([1, 1]) .x(xscale) .on('zoom', function() { svg.select('.data').attr('d', line) }); svg.call(zoom); // Scrolling code END }); }
i found the issue in here. issue is in json data what you provided. that all json data stored as strings not as integers. all integer numbers with in the double quotes. that mean it become as a string. when the max number taking for the x axis from the json data, it take the 9 as max number. it happen because 9 is the max number when numbers are strings. because of that graph not going to continue after 9. but expected value is 12. no need the scrolling code here. simply can make the graph scrollable by adding css entry in div ("overflow: scroll")
Ideas for distinct layering of stacked graph in D3.js
i have data for the first and following attempt of seeking asylum in germany for refugees and therefore two graphs which are looking like this First one Second one Transition from one to another shoulnd't be a problem i suppose but what I want is so show both of them in one graph too. So a combination of both datasets for each country. But is it possible to distinguish from one another if I combine countries and the first and following ("erst/folge")? Each country should have on layer divided by two, one is the first attempt the other is the second attempt of this country. One idea to differ those sub-layers is maybe custom coloscale which I try right now. How can I show both data in one graph? Is it even possible to join both datasets and still see the difference? Here is my html: <!DOCTYPE html> <meta charset="utf-8"> <style> body { font: 10px sans-serif; } .chart { background: #fff; } p { font: 12px helvetica; } .axis path, .axis line { fill: none; stroke: #000; stroke-width: 2px; shape-rendering: crispEdges; } button { position: absolute; right: 50px; top: 10px; } </style> <body> <script src="http://d3js.org/d3.v2.js"></script> <div id="option"> <input name="updateButton" type="button" value="Show Data for second Attemp" onclick="updateSecond('folgeantraege_monatlich_2015_mitentscheidungenbisnovember.csv')" /> </div> <div id="option"> <input name="updateButton" type="button" value="Show Data for first Attemp" onclick="updateFirst('erstantraege_monatlich_2015_mitentscheidungenbisnovember.csv')" /> </div> <div id="option"> <input name="updateButton" type="button" value="Show both" onclick="updateBoth('erstantraege_monatlich_2015_mitentscheidungenbisnovember.csv', 'folgeantraege_monatlich_2015_mitentscheidungenbisnovember.csv')" /> </div> <div class="chart"> </div> <script> var margin = {top: 20, right: 40, bottom: 30, left: 30}; var width = document.body.clientWidth - margin.left - margin.right; var height = 400 - margin.top - margin.bottom; var x = d3.time.scale() .range([0, width]); var y = d3.scale.linear() .range([height-10, 0]); var dateParser = d3.time.format("%Y-%m-%d").parse; var stack = d3.layout.stack() .offset("zero") .values(function(d) { return d.values; }) .x(function(d) { return d.date; }) .y(function(d) { return d.value; }); var area = d3.svg.area() .interpolate("cardinal") .x(function(d) { return x(d.date); }) .y0(function(d) { return y(d.y0); }) .y1(function(d) { return y(d.y0 + d.y); }); var z = d3.scale.category20() chart("erstantraege_monatlich_2015_mitentscheidungenbisnovember.csv"); var datearray = []; var colorrange = []; function chart(csvpath) { // var dateParser = d3.time.format("%Y-%m-%d").parse; // var margin = {top: 20, right: 40, bottom: 30, left: 30}; // var width = document.body.clientWidth - margin.left - margin.right; // var height = 400 - margin.top - margin.bottom; // var x = d3.time.scale() // .range([0, width]); // // var y = d3.scale.linear() // .range([height-10, 0]); // var z = d3.scale.category20() //var color = d3.scale.category10() var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(d3.time.months); var yAxis = d3.svg.axis() .scale(y); var yAxisr = d3.svg.axis() .scale(y); // var stack = d3.layout.stack() // .offset("zero") // .values(function(d) { return d.values; }) // .x(function(d) { return d.date; }) // .y(function(d) { return d.value; }); var nest = d3.nest() .key(function(d) { return d.Land}); // var nestFiltered = nest.filter(function(d){ // return d.Land != 'Total'; // }) // var area = d3.svg.area() // .interpolate("cardinal") // .x(function(d) { return x(d.date); }) // .y0(function(d) { return y(d.y0); }) // .y1(function(d) { return y(d.y0 + d.y); }); var svg = d3.select(".chart").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 + ")"); d3.csv(csvpath, function(data) { data.forEach(function(d) { d.date = dateParser(d.Datum); d.value = +d.ErstanträgeZahl; }); //onsole.log(data); var layers = stack(nest.entries(data)); console.log(nest.entries(data)); x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]); svg.selectAll(".layer") .data(layers) .enter().append("path") .attr("class", "layer") .attr("d", function(d) { return area(d.values); }) .style("fill", function(d, i) { return z(i); }); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.append("g") .attr("class", "y axis") .attr("transform", "translate(" + width + ", 0)") .call(yAxis.orient("right")); svg.append("g") .attr("class", "y axis") .call(yAxis.orient("left")); }); } function updateSecond(csvpath) { var nest = d3.nest() .key(function(d) { return d.Land}); d3.csv(csvpath, function(data) { data.forEach(function(d) { d.date = dateParser(d.Datum); d.value = +d.Summe; console.log(d.date); console.log(d.value); }); var layers = stack(nest.entries(data)); x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]); d3.selectAll("path") .data(layers) .transition() .duration(750) .style("fill", function(d, i) { return z(i); }) .attr("d", function(d) { return area(d.values); }); svg.select(".y.axis") // change the y axis .duration(750) .call(yAxis); }); } function updateFirst(csvpath) { var nest = d3.nest() .key(function(d) { return d.Land}); d3.csv(csvpath, function(data) { data.forEach(function(d) { d.date = dateParser(d.Datum); d.value = +d.ErstanträgeZahl; console.log(d.date); console.log(d.value); }); var layers = stack(nest.entries(data)); x.domain(d3.extent(data, function(d) { return d.date; })); y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]); d3.selectAll("path") .data(layers) .transition() .duration(750) .style("fill", function(d, i) { return z(i); }) .attr("d", function(d) { return area(d.values); }); svg.select(".y.axis") // change the y axis .duration(750) .call(yAxis); }); } function updateBoth(csvpathFirst, csvpathSecond){ var nest = d3.nest() .key(function(d) { return d.Land}); d3.csv(csvpathFirst, function(data1) { d3.csv(csvpathSecond, function(data2) { }); }); } </script> And my data is here at my repo EDIT1: For example csv1 contains Datum,Land,Summe,Position,Antragsart,EntscheidungenInsgesamt,Asylberechtigt,Flüchtling,GewährungVonSubsidiäremSchutz,Abschiebungsverbot,UnbegrenzteAblehnungen,Ablehnung,sonstigeVerfahrenserledigungen 2015-01-01,Afghanistan,1129,5,Erst,418,0,105,4,58,66,6,179 2015-02-01,Afghanistan,969,5,Erst,849,9,186,16,100,131,10,397 2015-03-01,Afghanistan,885,5,Erst,1376,17,309,58,158,201,11,622 2015-04-01,Afghanistan,1119,6,Erst,1838,21,384,75,202,261,15,880 2015-05-01,Afghanistan,1151,6,Erst,2272,21,499,91,249,303,16,1093 2015-06-01,Afghanistan,2051,6,Erst,2911,23,683,132,313,377,19,1364 2015-07-01,Afghanistan,2104,6,Erst,3340,27,767,160,366,431,21,1568 2015-08-01,Afghanistan,2270,5,Erst,3660,28,922,172,409,453,23,1653 2015-09-01,Afghanistan,2724,4,Erst,4057,36,1049,201,455,475,26,1815 2015-10-01,Afghanistan,3770,4,Erst,4540,37,1188,234,516,538,29,1998 2015-11-01,Afghanistan,4929,0,Erst,5026,46,1340,253,620,623,49,2095 And csv2 contains Datum,Antragsart,Land,Summe,Position,Datum2,Position,Herkunft,Entscheidungeninsgesamt,Asylberechtigt,Prozent,Flüchtling,Pronzent,GewährungvonsubisdiäremSchutz,Prozent,Abschiebungsverbot,Prozent,UnbegrenzteAblehnungen,Prozent,Ablehnung,Prozent,keinweiteresverfahren,Prozent,sonstigeVerfahrenserledigungen,Prozent 2015-01-01,Folge,Afghanistan,33,10,2015-01-01,10,Afghanistan,29,0,0,5,17.2,2,6.9,8,27.6,0,0,0,0,1,3.4,13,44.8 2015-02-01,Folge,Afghanistan,29,10,2015-02-01,10,Afghanistan,81,0,0,13,16,4,4.9,22,27.2,0,0,0,0,10,12.3,32,39.5 2015-03-01,Folge,Afghanistan,41,9,2015-03-01,9,Afghanistan,135,0,0,21,15.6,10,7.4,37,27.4,1,0.7,0,0,23,17,43,31.9 2015-04-01,Folge,Afghanistan,25,10,2015-04-01,10,Afghanistan,165,0,0,34,20.6,12,7.3,41,24.8,4,2.4,0,0,30,18.2,44,26.7 2015-05-01,Folge,Afghanistan,37,9,2015-05-01,9,Afghanistan,212,0,0,54,25.5,12,5.7,50,23.6,4,1.9,0,0,32,15.1,60,28.3 2015-06-01,Folge,Afghanistan,35,9,2015-06-01,9,Afghanistan,261,0,0,72,27.6,17,6.5,59,22.6,6,2.3,0,0,35,13.4,72,27.6 2015-07-01,Folge,Afghanistan,35,9,2015-07-01,9,Afghanistan,288,0,0,82,28.5,17,5.9,64,22.2,6,2.1,0,0,42,14.6,77,26.7 2015-08-01,Folge,Afghanistan,34,9,2015-08-01,9,Afghanistan,321,0,0,100,31.2,20,6.2,66,20.6,6,1.9,0,0,52,16.2,77,24 2015-09-01,Folge,Afghanistan,27,4,2015-09-01,9,Afghanistan,354,0,0,120,33.9,20,5.6,72,20.3,7,2,0,0,54,15.3,81,22.9 2015-10-01,Folge,Afghanistan,24,9,2015-10-01,9,Afghanistan,389,0,0,136,35,20,5.1,83,21.3,7,1.8,0,0,54,13.9,89,22.9 2015-11-01,Folge,Afghanistan,47,,,,,431,1,0.2,148,34.3,23,5.3,97,22.5,8,1.9,0,0,58,13.5,96,22.3 The values ("Summe") should sum up the values per month of each country (Afghanistan) but also should reflect the values for their stacks on their own (Right now I'm trying to figur out how to use texture.js and custom scales to use textures to distinguish the colors from another because every country should have it's own color in this graph but as I already mentioned they should be different in their sublayers When I try to put both csv fiels in one file I get not exactly but something similar to this Can you give me some tips how to archive sub-layers (data structure/algorithm or what i takes to achieve this) so I can proceed and try to implement textures? Thanks in advance Final EDIT as answer to Cyrils : var margin = {top: 20, right: 40, bottom: 30, left: 30}; var width = document.body.clientWidth - margin.left - margin.right; var height = 400 - margin.top - margin.bottom; var x = d3.time.scale() .range([0, width]); var y = d3.scale.linear() .range([height-10, 0]); var dateParser = d3.time.format("%Y-%m-%d").parse; var stack = d3.layout.stack() .offset("zero") .values(function(d) { return d.values; }) .x(function(d) { return d.graphDate; }) .y(function(d) { return d.value; }); var area = d3.svg.area() .interpolate("cardinal") .x(function(d) { return x(d.graphDate); }) .y0(function(d) { return y(d.y0); }) .y1(function(d) { return y(d.y0 + d.y); }); var z = d3.scale.category20() doInit(); updateFirst('data/all.csv'); function doInit(){ //make the svg and axis xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(d3.time.months); yAxis = d3.svg.axis() .scale(y); yAxisr = d3.svg.axis() .scale(y); //make svg var graph = d3.select(".chart").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 + ")"); graph.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); graph.append("g") .attr("class", "y axis yright") .attr("transform", "translate(" + width + ", 0)") .call(yAxis.orient("right")); graph.append("g") .attr("class", "y axis yleft") .call(yAxis.orient("left")); } function updateFirst(csvpath) { var nest = d3.nest() .key(function(d) { return d.Land+ "-" + d.Antragsart}); //console.log(nest); d3.csv(csvpath, function(data) { data.forEach(function(d) { //console.log(data); d.graphDate = dateParser(d.Datum); d.value = +d.Summe; d.type= d.Antragsart; }); var layers = stack(nest.entries(data)).sort(function(a,b){return d3.ascending(a.key, b.key)}); console.log(layers); x.domain(d3.extent(data, function(d) { return d.graphDate; })); y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]); var k = d3.select("g .x") .call(xAxis); d3.select("g .yright") .call(yAxis); d3.select("g .yleft") .call(yAxis); d3.selectAll("defs").remove(); d3.select(".chart svg g").selectAll("path").remove(); d3.select(".chart svg g").selectAll("path") .data(layers).enter().append("path") //.style("fill", function(d, i) { console.log(d.key);return z(d.key); }) .attr("class", function(d){ var country = d.key.split("-")[0]; var src = d.key.split("-")[1]; return src; }) .style("fill", function(d){ var country = d.key.split("-")[0]; var src = d.key.split("-")[1]; if (src === "Folge"){ var t = textures.lines().thicker(2).stroke(z(country)); d3.select(".chart").select("svg").call(t); return t.url(); } else { return z(country); } }) .attr("d", function(d) { return area(d.values); }); }); }
For merging the records I am making use of d3 queue..Read here The purpose of this is to load 2 CSV via ajax and when both loaded calls the callback. queue() .defer(d3.csv, csvpathFirst) //using queue so that callback is called after loading both CSVs .defer(d3.csv, csvpathSecond) .await(makeMyChart); function makeMyChart(error, first, second) { var data = []; Make the nested function based on country and csv var nest = d3.nest() .key(function(d) { return d.Land + "-" + d.src; //d.src is first if first csv second if vice versa }); Next I am merging the records like this: //iterate first first.forEach(function(d) { d.graphDate = dateParser(d.Datum); d.value = +d.Summe; d.src = "first" data.push(d) }); //iterate second second.forEach(function(d) { d.graphDate = dateParser(d.Datum); d.value = +d.Summe; d.src = "second" data.push(d) }); //sort layers on basis of country var layers = stack(nest.entries(data)).sort(function(a, b) { return d3.ascending(a.key, b.key) }); Regenerate the axis like this: //regenerate the axis with new domains var k = d3.select("g .x") .call(xAxis); d3.select("g .yright") .call(yAxis); d3.select("g .yleft") .call(yAxis); Remove all old paths and defs DOM like this: d3.selectAll("defs").remove(); d3.select(".chart svg g").selectAll("path").remove(); Next based on country and first csv and second csv add style fill. .style("fill", function(d) { var country = d.key.split("-")[0]; var src = d.key.split("-")[1]; if (src === "first") { //use texture.js for pattern var t = textures.lines().thicker().stroke(z(country)); d3.select(".chart").select("svg").call(t); return t.url(); } else { return z(country); } }) Working code here Hope this helps!
Controlling bar position with tickValues in d3.js
I'm trying to build a multi series bar chart using d3 but running into problems due to the sparse nature of the dataset. I want to force the x-axis to have a tick for every day, even if there is no data. The test data I have can have data points that are weeks apart so I'm expecting wide areas with no bars - which is fine. I thought I could force the xAxis to use a set of predefined ticks using the tickValues array, but these leads to very strange display of overlaying the text for each day on top of days that do have some data. I've included a screenshot of what I mean. I get the feeling I'm supposed to do something when calculating the width of the bars but can't figure out what that might be. Code: var data = []; var tickValues = []; var max = _.max(chartData.tabular, function(assessment) { return assessment.dateUTC; }); var min = _.min(chartData.tabular, function(assessment) { return assessment.dateUTC; }); var iter = moment.twix(min.dateUTC, max.dateUTC).iterate("days"); while(iter.hasNext()){ var momentObj = iter.next(); var assessment = _.find(chartData.tabular, {'date': momentObj.format('DD/MM/YYYY')}); tickValues.push(momentObj.valueOf()); if(assessment != null){ if(assessment.type == 'calculated'){ data.push({date: momentObj.valueOf(), calculated: assessment.score, manual: null}); } if(assessment.type == 'manual'){ data.push({date: momentObj.valueOf(), calculated: null, manual: assessment.score}); } } } log(data); var margin = {top: 20, right: 55, bottom: 30, left: 40}, width = $('#cahai-chart').width() - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var x = d3.scale.ordinal() .rangeRoundBands([0, width], .1); var y = d3.scale.linear() .rangeRound([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .tickValues(tickValues) .tickFormat(function(d){return d3.time.format('%d/%m/%y')(new Date(d))}); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var color = d3.scale.ordinal() .range(["#001c9c","#101b4d","#475003","#9c8305","#d3c47c"]); var svg = d3.select("#cahai-chart svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var labelVar = 'date'; var varNames = d3.keys(data[0]).filter(function (key) { return key !== labelVar;}); color.domain(varNames); data.forEach(function (d) { var y0 = 0; d.mapping = varNames.map(function (name) { return { name: name, label: d[labelVar], y0: y0, y1: y0 += +d[name] }; }); d.total = d.mapping[d.mapping.length - 1].y1; }); x.domain(data.map(function (d) { return d.date; })); y.domain([0, d3.max(data, function (d) { return d.total; })]); 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", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("Score"); var selection = svg.selectAll(".series") .data(data) .enter().append("g") .attr("class", "series") .attr("transform", function (d) { return "translate(" + x(d.date) + ",0)"; }); selection.selectAll("rect") .data(function (d) { return d.mapping; }) .enter().append("rect") .attr("width", x.rangeBand()) .attr("y", function (d) { return y(d.y1); }) .attr("height", function (d) { return y(d.y0) - y(d.y1); }) .style("fill", function (d) { return color(d.name); }) .style("stroke", "grey") .on("mouseover", function (d) { showPopover.call(this, d); }) .on("mouseout", function (d) { removePopovers(); }) var legend = svg.selectAll(".legend") .data(varNames.slice().reverse()) .enter().append("g") .attr("class", "legend") .attr("transform", function (d, i) { return "translate(55," + i * 20 + ")"; }); legend.append("rect") .attr("x", width - 10) .attr("width", 10) .attr("height", 10) .style("fill", color) .style("stroke", "grey"); legend.append("text") .attr("x", width - 12) .attr("y", 6) .attr("dy", ".35em") .style("text-anchor", "end") .text(function (d) { return d; }); function removePopovers () { $('.popover').each(function() { $(this).remove(); }); } function showPopover (d) { $(this).popover({ title: d.name, placement: 'auto top', container: 'body', trigger: 'manual', html : true, content: function() { return "Date: " + d3.time.format('%d/%m/%y')(new Date(d.label)) + "<br/>Score: " + d3.format(",")(d.value ? d.value: d.y1 - d.y0); } }); $(this).popover('show') }
An ordinal scale will always show as many ticks are there are values in the domain. You just need to pass the full array of dates as the domain. Replace this line x.domain(data.map(function (d) { return d.date; })); with this x.domain(tickValues); It looks like you have everything else set up correctly, so this will space the bars out along the axis and make them slimmer.
d3 multiple y axis for multiple bar chart
I have built a chart like this example http://bl.ocks.org/mbostock/4679202 here I would like to have y-axis labels for each series the below code is my code to generate graph since its a stacked layout. it feels a little complex for me imagine the y-axis transform attribute for each series var margin = {top: 10, right: 5, bottom: -6, left: 5}; var width = $(div).width(), height = $(div).height() - margin.top - margin.bottom; var svg = d3.select(div).append('svg') .attr('width',width) .attr('height',height) .append("g") .attr("transform", "translate("+margin.left+"," + margin.top + ")");; var nest = d3.nest() .key(function(d) { return d.group; }); var stack = d3.layout.stack() .values(function(d) { return d.values; }) .x(function(d) { return d.date; }) .y(function(d) { return d.value; }) .out(function(d, y0) { d.valueOffset = y0; }); var dataByGroup = nest.entries(data); stack(dataByGroup); var x = d3.scale.ordinal() .rangeRoundBands([0, width-7], .25, 0); var y0 = d3.scale.ordinal() .rangeRoundBands([height-15, 0], .2); var timeFormat = d3.time.format("%c"); var MinuteNameFormat = d3.time.format("%H:%M"); var y1 = d3.scale.linear(); var formatDate = function(d,i) {if(i%3 == 0){var date = new Date(parseInt(d)); return MinuteNameFormat(date);}else{return "";} }; var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(d3.time.minutes,15) .tickFormat(formatDate); var yAxis = d3.svg.axis() .scale(y1) .orient("Left") .ticks(0) .tickFormat(formalLabel); x.domain(dataByGroup[0].values.map(function(d) { return d.date; })); y0.domain(dataByGroup.map(function(d) { return d.key; })); y1.domain([0, d3.max(data, function(d) { return d.value; })]).range([y0.rangeBand(), 0]); var tooltip = d3.select(div) .append("div") .style("position", "absolute") .style("z-index", "10").attr("class","tooltip") .style("visibility", "hidden") .style("background","#fff") .style("border","1px solid #CCC") .style("font-size", "11px") .style("padding","11px") .text("a simple tooltip"); var group = svg.selectAll(".group") .data(dataByGroup) .enter().append("g") .attr("class", "group") .attr("transform", function(d) { return "translate(0," + y0(d.key) + ")"; }); group.filter(function(d, i) { return !i; }).append("g") .attr("class", "y axis") .attr("transform", "translate(0," + y0.rangeBand() + ")") .call(yAxis); var tipText var bars = group.selectAll("rect") .data(function(d) { return d.values; }) .enter().append("g"); bars.append("rect") .style("fill", function(d,i) { return getColor(d.ip); }) .attr("x", function(d) { return x(d.date); }) .attr("y", function(d) { return y1(d.value); }) .attr("width",x.rangeBand()) //x.rangeBand() .attr("height", function(d) { return y0.rangeBand() - y1(d.value); }); group.filter(function(d, i) { return !i; }).append("g") .attr("class", "x axis") .attr("transform", "translate(0," + y0.rangeBand() + ")") .call(xAxis); this is the code for my d3 graph can anyone suggest ??
This is entirely possible, but it would clutter you graph if you have more than 2 y-axis, one on the left side and one on the right. Here is an example: http://bl.ocks.org/benjchristensen/2579619 You declare an axis with this code: var yAxis = d3.svg.axis() .scale(y1) .orient("Left") .ticks(0) .tickFormat(formalLabel); You just need to put 1 of this for each element, and you also need to declare a new y1,y2,y3... etc for each element. And finally you need to make sure each data is bound to a different axis y1,y2,y3... etc Hope this helps.