Add new D3 chart above older ones - javascript

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")

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...
}

Multiple d3 charts on a page with different (linear and log) axes

I've built a page with five d3.js charts. Four of them use standard linear axes (linear data over time), and the fifth chart uses log/log axes. So far, I can get either the linear charts to render correctly, or the log/log chart to render correctly, but not both.
Question -- how can I define log/log axes for the final chart while keeping the first four linear charts working correctly?
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
// Overall chart settings
var divwidth = $("#collapseOne").width();
var divheight = $(window).height();
var margin = {top: 25, right: 60, bottom: 35, left: 80},
width = divwidth - margin.left - margin.right,
height = 0.65*divheight - margin.top - margin.bottom;
var formatDate = d3.time.format("%Y-%m-%d");
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");
</script>
Then I define the first chart:
<script>
// Chart1 - NumofCompanies chart settings
var line1 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.cumcos); });
var chart1 = d3.select("#collapseOne")
.append("div")
.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.tsv("/monthly.tsv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.cumcos; }));
chart1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart1.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("Number of companies");
chart1.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line1);
});
function type(d) {
d.date = formatDate.parse(d.date);
d.cumcos = +d.cumcos;
return d;
}
</script>
and then the log/log chart
<script>
// Chart5 - Log/log chart
var x = d3.scale.log()
.range([0, width]);
var y = d3.scale.log()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line5 = d3.svg.line()
.x(function(de) { return x(de.ordernum); })
.y(function(de) { return y(de.sumfunding); });
var chart5 = d3.select("#collapseFive").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.tsv("/loglog.tsv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(de) { return de.ordernum; }));
y.domain(d3.extent(data, function(de) { return de.sumfunding; }));
chart5.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart5.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("Sum of Funding ($)");
chart5.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line5);
});
function type(de) {
de.ordernum = +de.ordernum;
de.sumfunding = +de.sumfunding;
return de;
}
</script>
edit - I've also tried renaming variables like this below and it still doesn't work; the axes are still linear. And when I try changing things like x.domain to xlog.domain and .x(function(de to .xlog(function(de then nothing on the chart renders at all.
<script>
// Chart5 - Log/log chart
var xlog = d3.scale.log()
.range([0, width]);
var ylog = d3.scale.log()
.range([height, 0]);
var xAxislog = d3.svg.axis()
.scale(xlog)
.orient("bottom");
var yAxislog = d3.svg.axis()
.scale(ylog)
.orient("left");
var line5 = d3.svg.line()
.x(function(de) { return x(de.ordernum); })
.y(function(de) { return y(de.sumfunding); });
var chart5 = d3.select("#collapseFive").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.tsv("/loglog.tsv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(de) { return de.ordernum; }));
y.domain(d3.extent(data, function(de) { return de.sumfunding; }));
chart5.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxislog);
chart5.append("g")
.attr("class", "y axis")
.call(yAxislog)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Sum of Funding ($)");
chart5.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line5);
});
function type(de) {
de.ordernum = +de.ordernum;
de.sumfunding = +de.sumfunding;
return de;
}
</script>
You are mistaking the first x in the line generator with the variable x in your scale:
var line5 = d3.svg.line()
.x(function(de) { return x(de.ordernum); })
//^--this has to be "x" ^--this is the scale
So, it has to be:
var line5 = d3.svg.line()
.x(function(de) { return xLog(de.ordernum); })
.y(function(de) { return yLog(de.sumfunding); });
According to the API of version 3:
line.x - get or set the x-coordinate accessor.
line.y - get or set the y-coordinate accessor.
As a good practice, avoid variable names like x. Instead, use names like xScale, xAxis, xPosition etc.

D3 line and chart graphs on the same page breaking

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.

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) {

Line chart in D3 is all over the place

everyone! Currently trying to generate a line chart using D3 with a simple JSON data. I'm using mbostock's line chart code, I just change data.tsv with my own JSON file:
[
{"date":"Aug-14","close":"1"},
{"date":"Feb-14","close":"2"},
{"date":"Jan-14","close":"1"},
{"date":"Jul-14","close":"3"},
{"date":"Jun-14","close":"1"},
{"date":"Mar-14","close":"3"},
{"date":"May-14","close":"1"},
{"date":"Nov-14","close":"1"},
{"date":"Oct-14","close":"1"},
{"date":"Sep-14","close":"1"}
]
However, I get a graph that looks like this crazy line chart
And I have no idea why it's drawing the lines like that. Any input would be amazing!
Edit: Adding the current code I'm using.
<script>
//Setting up...
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("%d-%b-%y").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").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("test.php", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
console.log(d.date);
});
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);
});
</script>
Hard to tell without looking directly at your code, but you might not be passing a Number into your scale function. In the source code you copied, see:
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
Or, just pass in data where close is a Number.
[
{"date":"Aug-14","close":1},
{"date":"Feb-14","close":2},
{"date":"Jan-14","close":1},
{"date":"Jul-14","close":3},
{"date":"Jun-14","close":1},
{"date":"Mar-14","close":3},
{"date":"May-14","close":1},
{"date":"Nov-14","close":1},
{"date":"Oct-14","close":1},
{"date":"Sep-14","close":1}
]
Edit:
Note also that the dates in the example you linked are sorted, yours are not.
var parseDate = d3.time.format("%b-%y").parse;
data.sort(function(a, b) {
return d3.ascending(parseDate(a.date), parseDate(b.date));
});

Categories