how to take json from veriable in d3 chart - json

I am using D3 to plot bubble chart using data in json format to plot bubble chart
My json format is like:{"children":[
{"name":"1","size":0.5},
{"name":"3","size":0.3636},
{"name":"4","size":0.5652},
{"name":"5","size":19.5556},
{"name":"6","size":1.037},
{"name":"7","size":0.7048},
{"name":"8","size":2.2593},
{"name":"9","size":13.7407},
{"name":"10","size":4.2222},
{"name":"11","size":2.6667},
{"name":"12","size":0.7037},
{"name":"13","size":20.6296},
{"name":"14","size":17.7037},
{"name":"15","size":2.1481},
{"name":"16","size":3.4815},
{"name":"17","size":0.1852},
{"name":"18","size":0.087},
{"name":"19","size":15.68}
]}
I am accessing this json as:
d3.json("flare.json", function(error, root) {
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root))
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
now i want to access from veriable
var datasource={"children":[
{"name":"1","size":0.5},
{"name":"3","size":0.3636},
{"name":"4","size":0.5652},
{"name":"5","size":19.5556},
{"name":"6","size":1.037},
{"name":"7","size":0.7048},
{"name":"8","size":2.2593},
{"name":"9","size":13.7407},
{"name":"10","size":4.2222},
{"name":"11","size":2.6667},
{"name":"12","size":0.7037},
{"name":"13","size":20.6296},
{"name":"14","size":17.7037},
{"name":"15","size":2.1481},
{"name":"16","size":3.4815},
{"name":"17","size":0.1852},
{"name":"18","size":0.087},
{"name":"19","size":15.68}
]}
I have tried like:
JSON.parse("dataSource", function(error, root) {
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root))
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
but unable to get result where i am going wrong.please reply me.
Thanks
prashansa

datasource is variable not string
so try this
JSON.parse(dataSource, function(error, root) {
EDITED
var dataSource = '{"children":...'; //JSON.parse needed
//so your code would be
JSON.parse(dataSource, function(error, root) {
var dataSource = {"children":...}; //JSON.parse not needed
//so your code would be
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(dataSource))
.filter(function(d) { return !d.children; }))

Related

Save selected D3.js Treemap nodes to local file

I am trying to finalize a D3.js treemp using this example code: https://bl.ocks.org/mbostock/4339083
I am running a local "python -m SimpleHTTPServer" web instance to serve up my pages.
I am not proficient in HTML or JavaScript.
I have a very large local JSON file that I created in python that is referenced in the "d3.json("d3_flare.json", function(error, flare)" in the index.html code snippet.
I need assistance with two items please:
I have very long text on the 4th node down from the JSON root node that D3.js & HTML will process. The text looks like:
"name": "Some Title: Some very very long text...Some very very long text...Some very very long text...Some very very long text...Some very very long text."
but can be longer or shorter.
How can I wrap this text to be more legible and neat while viewing in the browser at this position of JSON being read by D3.js & HTML given my attached index.html code snippet?
I need to save every selected node path(s) to a local file (e.g. CSV) that the user has clicked on, remember only running a local "python -m SimpleHTTPServer" web instance to serve up my pages.
How can I write the selected node paths to a local file given my attached index.html code snippet?
Thank you
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1.5px;
}
</style>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
/*var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 800 - margin.top - margin.bottom;
*/
var margin = {top: 40, right: 120, bottom: 40, left: 500},
width = 10000 - margin.right - margin.left,
height = 775 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("d3_flare.json", function(error, flare) {
if (error) throw error;
root = flare;
root.x0 = height / 2;
root.y0 = 0;
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
});
d3.select(self.frameElement).style("height", "800px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth original was 180
nodes.forEach(function(d) { d.y = d.depth * 900; });
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("text")
.attr("y", function(d) { return d.children || d._children ? -13 : 13; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.name; })
.style("fill-opacity", 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("circle")
.attr("r", 4.5)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
</script>
To solve my second question I ended up adding a contextmenu to the var nodeEnter section of my previously posted code where users are directed only to right-click a certain node in the path that gets recorded. The contextmenu:
.on("contextmenu", function (d) {
d3.event.preventDefault()
sessionStorage.setItem("selected_node", JSON.stringify(d.name));
var val = sessionStorage.getItem("selected_node");
post_session(val);
});
I then passed the sessionStorage objects to this function where I have cgi/bin python writing the requests to a file:
function post_session(session_store_info)
{
var http = new XMLHttpRequest();
http.open('POST', 'cgi-bin/post_session', true);
http.setRequestHeader('Content-Type', 'text/plain; charset=UTF-8');
http.send(session_store_info);
return http.status!=404;
}

D3 linegraph using JSON data from API

I am working on making a linegraph in D3 that displays data in JSON format that is retrieved from an API.
I found an example line graph project on D3's website and it seems relatively straight forward. The main difference between the example and my project is that the example uses data from a local csv file instead of JSON from an API request.
// This needs to be changed to my API request
d3.tsv("data.tsv", function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
return d;
}, 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.close; }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
});
I changed the csv request part to this because I'm trying to load in my JSON from the API:
d3.json(url).get(function(d) {
for(var i = 0; i < d.Data.length; i++) {
coinValue.push(d.Data[i].close);
dates.push(convertUnixTime(d.Data[i].time));
}
return d;
}, function(error, data) {
if (error) throw error;
This isn't working. I'm confused about why there's a comma and then another function right after.
What is the proper way to make the exact same line graph but with a d3.json function/API request instead?
My JSON looks like this: https://min-api.cryptocompare.com/data/histoday?fsym=ETH&tsym=USD&limit=2&aggregate=3&e=CCCAGG
The difference between d3.csv and d3.json is the accessor (2nd argument).
d3.csv: d3.csv(url[[, row], callback]) in which row is nothing but each row of the data fetched i.e. if you add a function as follows:
d3.csv(url, function(d) {
// each row of the data
}, callback)
d3.json: d3.json(url[, callback]) DOES NOT provide this each row accessor. But you can do that within the callback as follows:
d3.json(url, function(err, data) {
data.forEach(function(row) {
// parse each row as required
});
})
Using the above syntax along with the provided example code and JSON URL, here's a code snippet drawing a line chart:
// This needs to be changed to my API request
d3.json("https://min-api.cryptocompare.com/data/histoday?fsym=ETH&tsym=USD&limit=2&aggregate=3&e=CCCAGG", function(error, d) {
var data = d.Data;
data.forEach(function(d){ d.time = new Date(d.time * 1000) });
//console.log(data);
if (error) throw error;
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseTime = d3.timeParse("%d-%b-%y");
var x = d3.scaleTime()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var line = d3.line()
.x(function(d) { return x(d.time); })
.y(function(d) { return y(d.close); });
x.domain(d3.extent(data, function(d) { return d.time; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>
Hope this helps.

How to transform code from a tsv import to direct data

I'm working with some D3 examples in php driven pages. I've found a great example I want to use of a line chart with inline lables (full code here: https://bl.ocks.org/mbostock/4b66c0d9be9a0d56484e), but I can't figure out how to transition the code from a tsv import to an array provided directly from the database.
I am obviously providing the data directly like this:
var data = [
{date:2009, Apples:130, Bananas:40},
{date:2010, Apples:137, Bananas:58},
{date:2011, Apples:166, Bananas:97},
{date:2012, Apples:154, Bananas:117},
{date:2013, Apples:179, Bananas:98},
{date:2014, Apples:187, Bananas:120},
{date:2015, Apples:189, Bananas:84}
]
And then I'm trying to replace this chunk of code that handles the import and the sorting into an array automatically.
d3.requestTsv("data.tsv", function(d) {
d.date = parseTime(d.date);
for (var k in d) if (k !== "date") d[k] = +d[k];
return d;
}, function(error, data) {
if (error) throw error;
var series = data.columns.slice(1).map(function(key) {
return data.map(function(d) {
return {
key: key,
date: d.date,
value: d[key]
};
});
});
But I think I'm having problems replicating the portion that creates the series.
I've tried several variations of this:
var series = data.map(function(key) {
return data.map(function(d) {
return {
key: key,
date: d.date,
value: d[key]
};
});
});
followed with a function to set the data type at the end of the code:
function type(d) {
d.date = parseTime(d.date);
for (var k in d) if (k !== "date") d[k] = +d[k];
return d;
}
But nothing seems to work. I'm sure there is something simple I'm missing, but what should I specifically be changing here to use code by providing the data directly in an array instead of a tsv import?
Your data array is correct (regarding the TSV in Bostock's code).
However, you have two problems:
The d3.tsv function creates an array property named columns. Since you're ditching d3.tsv and using a variable to store the data, you'll have to create that array yourself:
data.columns = ["date", "Apples", "Bananas"]
The d3.tsv accepts a row function. Again, since you're using a variable to store the data, you'll have to use a forEach to do what the row function does in Bostock's code:
data.forEach(d=>{
d.date = parseTime(d.date);
for (var k in d) if (k !== "date") d[k] = +d[k];
});
Here is the updated code using a variable to store the data: https://bl.ocks.org/anonymous/749f2c5bc6a42d68bca3ec579646ff1d
And here the same code in the Stack snippet:
<style>
text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke-width: 1.5px;
}
.label {
text-anchor: middle;
}
.label rect {
fill: white;
}
.label-key {
font-weight: bold;
}
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.0.0-alpha.9.min.js"></script>
<script>
var parseTime = d3.timeParse("%Y");
var svg = d3.select("svg");
var margin = {top: 30, right: 50, bottom: 30, left: 30},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
labelPadding = 3;
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = [
{date:2009, Apples:130, Bananas:40},
{date:2010, Apples:137, Bananas:58},
{date:2011, Apples:166, Bananas:97},
{date:2012, Apples:154, Bananas:117},
{date:2013, Apples:179, Bananas:98},
{date:2014, Apples:187, Bananas:120},
{date:2015, Apples:189, Bananas:84}
];
data.columns = ["date", "Apples", "Bananas"]
data.forEach(d=>{
d.date = parseTime(d.date);
for (var k in d) if (k !== "date") d[k] = +d[k];
});
var series = data.columns.slice(1).map(function(key) {
return data.map(function(d) {
return {
key: key,
date: d.date,
value: d[key]
};
});
});
var x = d3.scaleTime()
.domain([data[0].date, data[data.length - 1].date])
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, d3.max(series, function(s) { return d3.max(s, function(d) { return d.value; }); })])
.range([height, 0]);
var z = d3.scaleCategory10();
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
var serie = g.selectAll(".serie")
.data(series)
.enter().append("g")
.attr("class", "serie");
serie.append("path")
.attr("class", "line")
.style("stroke", function(d) { return z(d[0].key); })
.attr("d", d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); }));
var label = serie.selectAll(".label")
.data(function(d) { return d; })
.enter().append("g")
.attr("class", "label")
.attr("transform", function(d, i) { return "translate(" + x(d.date) + "," + y(d.value) + ")"; });
label.append("text")
.attr("dy", ".35em")
.text(function(d) { return d.value; })
.filter(function(d, i) { return i === data.length - 1; })
.append("tspan")
.attr("class", "label-key")
.text(function(d) { return " " + d.key; });
label.append("rect", "text")
.datum(function() { return this.nextSibling.getBBox(); })
.attr("x", function(d) { return d.x - labelPadding; })
.attr("y", function(d) { return d.y - labelPadding; })
.attr("width", function(d) { return d.width + 2 * labelPadding; })
.attr("height", function(d) { return d.height + 2 * labelPadding; });
</script>

Using dynamic input(CSV) in d3js

I am trying to use dynamic input to a draw function in d3js. So when the user changes the csv it would remove the current selection and draw the visualization for the new input. So my question is would I be using a onChange function with the select and then within this function parse the csv and call for the draw function.The current working code is here in plunker:
https://plnkr.co/edit/AjVBK3rTOF5aI4eDDbV5?p=preview
<svg width="1250" height="1080"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width");
var format = d3.format(",d");
var color = d3.scaleOrdinal(d3.schemeCategory10);
var pack = d3.pack()
.size([width, width])
.padding(1.5);
var inputs = {};
function selectCity(){
//storing the drop-dsown selection in the ddSelection var
var ddSelection = document.getElementById("city").value;
//feeding that to create the csv filename you want
var str1 = ddSelection;
var str2 = ".csv";
var csvFile = str1.concat(str2);
str1.concat(str2);
console.log(csvFile);
d3.csv(csvFile, function(d) {
d.sno = +d.sno;
return d;
}, function(error, data) {
if (error) throw error;
d3.selectAll("input").on("change", function(){
inputs[this.id] = +this.value;
console.log(inputs.myValue + "-" + inputs.myRating)
if(inputs.myValue && inputs.myRating){
var classes = data.filter(d => d.value < inputs.myValue && d.rating >= inputs.myRating);
draw(classes);
}
})
function draw(classes) {
console.log(classes.length);
var root = d3.hierarchy({
children: classes
})
.sum(function(d) {
return d.value;
})
.each(function(d) {
if (id = d.data.id) {
var id, i = id.lastIndexOf(".");
d.id = id;
d.package = id.slice(0, i);
d.class = id.slice(i + 1);
}
});
var node = svg.selectAll(".node")
.data(pack(root).leaves())
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.append("circle")
.attr("id", function(d) {
return d.id;
})
.attr("r", function(d) {
return d.r;
})
.style("fill", function(d) {
return color(d.package);
});
node.append("clipPath")
.attr("id", function(d) {
return "clip-" + d.id;
})
.append("use")
.attr("xlink:href", function(d) {
return "#" + d.id;
});
node.append("text")
.attr("clip-path", function(d) {
return "url(#clip-" + d.id + ")";
})
.selectAll("tspan")
.data(function(d) {
return d.class.split(/(?=[A-Z][^A-Z])/g);
})
.enter().append("tspan")
.attr("x", 0)
.attr("y", function(d, i, nodes) {
return 13 + (i - nodes.length / 2 - 0.5) * 10;
})
.text(function(d) {
return d;
});
node.append("title")
.text(function(d) {
return d.data.id + "\n" + format(d.value);
});
}
});
}
</script>
</div>
Here is one example how to do it: http://www.d3noob.org/2014/04/using-html-inputs-with-d3js.html
You don't have to redraw everything but update certain elements.
I don't understand your part about changing the CSV. The user does not change the CSV but your visual output is depending on some user data. So yes, within the callback function of d3.csv(), you write your code that calls some kind of a draw function. But the draw function does not have to be defined there. You can write the function outside and just call it there. This increased the readable of your code dramatically. ;)

Django d3.js in HTML file not loading unless I manually open HTML file

I'm trying to implement a bubble chart on a webpage using the Django framework and the D3.js framework. I have an HTML file, and my data is stored in a JSON file.
Accessing the JSON file seems fine, because when I manually open the HTML file in my browser it works/loads fine, however, when I hit the webpage URL endpoint via Django, the D3 part of the page just shows blank (console says "node is undefined"). I am using the example code given here.
Any help would be very appreciated. Thanks.
D3 js code:
<script src="http://d3js.org/d3.v3.min.js"></script>
<div class="cloud">
<script>
var diameter = 960,
format = d3.format(",d"),
color = d3.scale.category20c();
var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameter])
.padding(1.5);
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("class", "bubble");
d3.json("cloud.json", function(error, root) {
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root))
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.append("title")
.text(function(d) { return d.className + ": " + format(d.value); });
node.append("circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return color(d.packageName); });
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.className.substring(0, d.r / 3); });
});
// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) {
var classes = [];
function recurse(name, node) {
if (node.children) node.children.forEach(function(child) { recurse(node.name, child); });
else classes.push({packageName: name, className: node.name, value: node.size});
}
recurse(null, root);
return {children: classes};
}
d3.select(self.frameElement).style("height", diameter + "px");
</script>