D3 line and chart graphs on the same page breaking - javascript
I am trying to render 2 graphs on the same page: chart graph and a linear graph
in html i have two divs which bind to the 2 different graphs respectively like so:
<div id="svg-container">
<div id="svg-container-avg">
The first graph that i import in html is this linear 'average' graph that just doesn't display the path or the x axis.
It works fine if i delete the chart graph which is imported straight after. I would automatically assume that there are some sort of dependencies between the two graphs, but i cannot find anything...
my first import, linear graph: scns-avg.js file contains this
// Set the dimensions of the canvas / graph
var margin = {
top: 30,
right: 20,
bottom: 70,
left: 50
},
width = 800 - margin.left - margin.right,
height = 350 - margin.top - margin.bottom;
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var valueline = d3.svg.line()
.x(function(d) {
return x(d.scnsID);
})
.y(function(d) {
return y(d.average);
});
var svg2 = d3.select("#svg-container-avg")
.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 + ")");
// Get the data
d3.json("scns-avg-data-retrieval.php", function(error, data) {
data.forEach(function(d) {
d.scnsID = d.scnsID;
d.average = +d.average;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.scnsID;
}));
y.domain([0, d3.max(data, function(d) {
return d.average;
})]);
svg2.append("path") // Add the valueline path.
.style("stroke", "rgba(13, 183, 196, 0.9)")
.attr("d", valueline(data));
svg2.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.scnsID);
})
.attr("cy", function(d) {
return y(d.average);
})
svg2.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg2.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
});
My Data structor in JSON:
[{"date":"11-Mar-16","average":"3.18","scnsID":"2"},{"date":"12-Mar-16","average":"3.09","scnsID":"3"},{"date":"15-Mar-16","average":"3.16","scnsID":"4"},{"date":"17-Mar-16","average":"3.20","scnsID":"5"}]
When trying to run the html page with both graphs being imported the scns-avg.js throws up in the console Error: Invalid value for attribute d="M43,2.8124999999999902LNaN,15.46875000000002LNaN,5.6249999999999805L471,0"
which points to valueline(data) in this part of the code
svg2.append("path")
.style("stroke", "rgba(13, 183, 196, 0.9)")
.attr("d", valueline(data));
That is dependent on this piece of code, so the problem must be lying here, but for 6 hours now i can't find the solution to this..
var valueline = d3.svg.line()
.x(function(d) { return x(d.scnsID); })
.y(function(d) { return y(d.average); });
My chart graph that gets imported next and if disabled the linear graph renders properly is as follows below:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Frequency:</strong> <span style='color:rgba(13, 183, 196, 0.9)'>" + d.average + "</span>";
});
var svg = d3.select("#svg-container").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 + ")");
svg.call(tip);
// Get the data
d3.json("scns-data-retrieval.php", function(error, data) {
if (error) throw error;
data.forEach(function(d) {
d.question = d.question;
d.average = +d.average;
});
x.domain(data.map(function(d) { return d.question; }));
y.domain([0, 5]);
legendSpace = width/5;
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", 0 - margin.left)
.attr("x", 0 - (height / 3))
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Level of need for help:");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.question); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.average); })
.attr("height", function(d) { return height - y(d.average); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
});
function type(d) {
d.average = +d.average;
return d;
};
data structure in JSON:
[{"question":1,"average":3.3333333333333},{"question":2,"average":2.5},{"question":3,"average":4},{"question":4,"average":2.75},{"question":5,"average":2.75},{"question":6,"average":2.75},{"question":7,"average":3},{"question":8,"average":3},{"question":9,"average":2.75},{"question":10,"average":3.25},{"question":11,"average":3.25},{"question":12,"average":3.5},{"question":13,"average":3},{"question":14,"average":3.25},{"question":15,"average":3.5},{"question":16,"average":3.5},{"question":17,"average":3.25},{"question":18,"average":3.75},{"question":19,"average":3.5},{"question":20,"average":3},{"question":21,"average":3},{"question":22,"average":3.5},{"question":23,"average":3.25},{"question":24,"average":3.75},{"question":25,"average":3.75},{"question":26,"average":3.75},{"question":27,"average":3.5},{"question":28,"average":2.75},{"question":29,"average":2.25},{"question":30,"average":3.5},{"question":31,"average":3},{"question":32,"average":3}]
I can't do this anymore and it's driving me crazy so i am now turning to the power of stackoverflow!
After some mad research and trial and error i have fixed this problem.
When you are trying to render more than 1 graph, if they are of different type and not lets say - multiple linear graphs, but something like a linear graph with a bar chart the variable scope is getting mixed up and it's not restricted to the javascript file once it's imported into html.
To fix this problem every graph js file has to wrapped in (function(){ All your graph code goes here })();
This restricts the variable scope to just the function.
Related
d3 chart refresh duplicate
I´m using d3.js library but i´m having so problems with linear graphics. It looks like on each page refresh, the graphics is replicated, and after some running time i got lots of duplicated graphics. I´m also using static data (i entered some random data for the graphic to work). Can someone help me? Why is this happening? Any solutions? chart3-controller.js module.exports = function Chart3Ctrl($scope) { d3 = require('d3') $scope.chart = function() { var arrData = [ ["2012-10-02", 200], ["2012-10-09", 300], ["2012-10-12", 150] ]; var margin = { top: 20, right: 20, bottom: 30, left: 50 }, width = 960 - margin.left - margin.right, height = 500 - 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 xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var line = d3.svg.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.close); }); var svg = d3.select("#chart3").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 + ")"); var data = arrData.map(function(d) { return { date: parseDate(d[0]), close: d[1] }; }); console.log(data); x.domain(d3.extent(data, function(d) { return d.date; })); y.domain(d3.extent(data, function(d) { return d.close; })); 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("Price ($)"); svg.append("path") .datum(data) .attr("class", "line") .attr("d", line); } } chart3.pug .widget-container.fluid-height.stf-cpu(ng-controller='Chart3Ctrl') .heading i.fa span(translate) Chart 3 .widget-content.padded div#chart3(style='width:100%; height:100%;') {{ chart() }} Please any help welcome! :) Thanx
Have you tried to remove the chart at the top of the function? so; d3.selectAll('#chart3 svg').remove(); this would remove any instance of the chart before re drawing, a better approach might be to wrap inside an if statement to check if the svg exists first and then drawing it if it doesn't, using jquery this could be ; if(!$('#chart3 svg').length > 0) { run code here... }
D3 bar chart with variable width and variable length data up to 1000 data points
I am having a lot of trouble with D3 bar chart where the length of the data I get from search is variable. I used the D3 bar chart example and built this. However as data length goes up, the graph gets less and less legible. The problem seems to be the range gets confused when it is beyond 100 points or so. Code is below: function makegraph(data,ctype) { //var data=xdata.splice(-900) var gwidth=data.length*2; if(gwidth < 800) gwidth=800; //gwidth=800 var margin = {top: 9, right: 20, bottom: 30, left: 90}, width = gwidth - margin.left - margin.right, height = 300 - margin.top - margin.bottom; x = d3.scale.ordinal() .rangeRoundBands([0, width], .1); //x=d3.scale.linear().domain([0,1]).range(0,width) var y = d3.scale.linear() .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .tickFormat(function(d) { if (d % 10) return ""; else return d;}) var yAxis = d3.svg.axis() .scale(y) .orient("left"); // .ticks(10, "%"); d3.select("svg").remove() var svg = d3.select("#graphchart").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 + ")"); var line = d3.svg.line() .x(function(d) { return x(d.rowid); }) .y(function(d) { return y(d[ctype]); }); x.domain(data.map(function(d) { return d.rowid; })); var ymax=d3.max(data, function(d) { return d[ctype]; }); var ymin=d3.min(data, function(d) { return d[ctype]; }); y.domain([ymin, ymax]); 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", Math.log10(ymax)) .attr("dy", ".71em") .style("text-anchor", "end") .text("Bytes"); // console.log(x.rangeBand()) var bar = svg.selectAll(".bar") .data(data); bar.enter().append("rect") .attr("class", "bar") .attr("x", function(d) { return x(d.rowid); }) .attr("width", x.rangeBand()) .attr("y", function(d) { return y(d[ctype]); }) .attr("height", function(d) { var xya= height - y(d[ctype]); /* console.log(xya+":"+d[ctype]); */ return height - y(d[ctype]); }) /* .attr("title", function(d) { return d['bytes']+" Bytes at "+d.stime; }) */ .on("mouseover", function(d) { $("#graphinfo").html(d3.format('0,000')(d['bytes'])+" Bytes at "+d.stime) }) .on("mouseout",function(d) { $("#graphinfo").html(' ')}) .on("click",function(d) { tablegraph(d)}); $('.bar').tooltip() } Do you have suggestions how I can make this graph so that the bar chart will grow horizontally for larger data sets? Thanks for any help and suggestions! Vijay
Horizontal bar chart d3.js not showing label
I've an horizontal bar chart in d3.js and I would like to add the name like "y-label" for every bar of the chart. The original example of my bar chart is http://bl.ocks.org/mbostock/2368837 without negative values. So I modified it for my purpose var margin = {top: 40, right: 20, bottom: 100, left: 60}, width = 720 - margin.left - margin.right, height = 480 - margin.top - margin.bottom; var x_4 = d3.scale.linear() .range([0, width]) var y_4 = d3.scale.ordinal() .rangeRoundBands([0, height], .2); var xAxis_4 = d3.svg.axis() .scale(x_4) .orient("top"); var tip_4 = d3.tip() .attr('class', 'd3-tip') .offset([-10, 0]) .html(function(d) { return "<strong>Value:</strong> <span style='color:red'>" + d.ln_numfollowers + "</span>"; }) var sampleSVG_4 = d3.select("#LinkedIn").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 + ")") .call(tip_4); d3.csv("#routes.Assets.at("d3/linkedin_competitor_prova.csv")", type, function(error, data) { x_4.domain(d3.extent(data, function(d) { return d.ln_numfollowers; })).nice(); y_4.domain(data.map(function(d) { return d.organization_name; })); sampleSVG_4.selectAll(".bar") .data(data) .enter().append("rect") .attr("class", function(d) { return d.ln_numfollowers < 0 ? "bar negative" : "bar positive"; }) .attr("x", function(d) { return x_4(Math.min(0, d.ln_numfollowers)); }) .attr("y", function(d) { return y_4(d.organization_name); }) .attr("width", function(d) { return Math.abs(x_4(d.ln_numfollowers) - x_4(0)); }) .attr("height", y_4.rangeBand()) .on('mouseover', tip_4.show) .on('mouseout', tip_4.hide);; sampleSVG_4.append("g") .attr("class", "x axis") .call(xAxis_4); sampleSVG_4.append("g") .attr("class", "y axis") .append("line") .attr("x1", x_4(0)) .attr("x2", x_4(0)) .attr("y2", height) }); function type(d) { d.ln_numfollowers = +d.ln_numfollowers; return d; } The csv data file is: organization_name,ln_numfollowers Carrot.mx,100 CarJump,45 I don't know why the organization_name is not showing. As you can see, not even in the original example the label on the y axis are showing.
Couple of problems: 1.) You probably don't want to create your x-axis using extent. With your sample data this would create a chart from 45 to 100. You probably want to start it at zero. x_4.domain([0,d3.max(data, function(d) { return d.ln_numfollowers; })]); 2.) You don't actually create a conventional y-axis. This code: sampleSVG_4.append("g") .attr("class", "y axis") .append("line") .attr("x1", x_4(0)) .attr("x2", x_4(0)) .attr("y2", height) Is creating a y-axis that's just a line. It's not using the built-in d3axis creation. What you need is: var yAxis_4 = d3.svg.axis() .scale(y_4) .orient("left"); .... sampleSVG_4.append("g") .attr("class", "y axis") .call(yAxis_4); Example here.
Plotting Bar Chart from MySQL Database using D3.js
I am trying to plot a bar chart by taking two columns from my database and converting it to JSON using json_encode in PHP (probc.php file) I referred to this example here to construct a bar chart - BAR CHART EXAMPLE After doing so, I changed the attributes letters and frequency to the column names I wanted to use (fphour for x-axis and fpprob for y-axis), and also changed d3.tsv to d3.json and included the relevant file (probc.php). But I do not get any results on running my file. Just a blank vanilla page. Please help! Here's the code <script src="http://d3js.org/d3.v3.min.js"></script> <script> var margin = {top: 20, right: 20, bottom: 30, left: 40}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var x = d3.scale.ordinal() .rangeRoundBands([0, width], .1); var y = d3.scale.linear() .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(10, "%"); 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 + ")"); d3.json("probc.php", type, function(error, data) { x.domain(data.map(function(d) { return d.fphour; })); y.domain([0, d3.max(data, function(d) { return d.fpprob; })]); 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("Frequency"); svg.selectAll(".bar") .data(data) .enter().append("rect") .attr("class", "bar") .attr("x", function(d) { return x(d.fphour); }) .attr("width", x.rangeBand()) .attr("y", function(d) { return y(d.fpprob); }) .attr("height", function(d) { return height - y(d.fpprob); }); }); function type(d) { d.fpprob = +d.fpprob; return d; } </script>
Type should not be here . d3.json("probc.php", ***type***, function(error, data) { Remove type d3.json("probc.php", function(error, data) {
Add new D3 chart above older ones
I currently have a web app which adds a new d3 chart to the page upon every click of submit (after defining parameters). What I'd like to do is add the newest chart above the others and push the older ones down to allow space. It tried changing the svg append to prepend but it's obviously not going to be that simple! Here's my script; function chartGenerator(file_location) { var margin = {top: 20, right: 20, bottom: 30, left: 50}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse; 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"); var yAxis = d3.svg.axis() .scale(y) .orient("left"); var line = d3.svg.line() .x(function(d) { return x(d.date); }) .y(function(d) { return y(d.close); }); var svg = d3.select("body").prepend("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("{% static 'dgconnection/data.csv' %}", function(error, data) { data.forEach(function(d) { d.date = parseDate(d.date); d.close = +d.close; }); x.domain(d3.extent(data, function(d) { return d.date; })); y.domain(d3.extent(data, function(d) { return d.close; })); svg.prepend("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); svg.predend("g") .attr("class", "y axis") .call(yAxis) .prepend("text") .attr("transform", "rotate(-90)") .attr("y", 6) .attr("dy", ".71em") .style("text-anchor", "end") .text("Value"); svg.prepend("path") .datum(data) .attr("class", "line") .attr("d", line); }); }
You can prepend elements to existing elements in D3, but the function isn't called prepend(), but insert() and takes two arguments. The first is, like for .append(), the name of the element to insert. The second is a selector. The new element will be inserted before the element that matches the selector. In your case, the only code you need to change is this: var svg = d3.select("body").insert("svg", "svg")