Drawing voronoi diagram from a csv file with d3.js - csv

I want to draw voronoi diagram using http://mbostock.github.com/d3/ex/voronoi.html from a set of points in a csv file.
I have modified the code:
var w = 1200,
h = 800;
d3.text("ReMeRi_4_2_2_0.csv", function(datasetText) {
var vertices = d3.csv.parseRows(datasetText);
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h)
.attr("class", "BuPu");
svg.selectAll("path")
.data(d3.geom.voronoi(vertices))
.enter().append("path")
.attr("class", function(d, i) { return i ? "q" + (i % 9) + "-9" : null; })
.attr("d", function(d) { return "M" + d.join("L") + "Z"; })
svg.selectAll("circle")
.data(vertices.slice(1))
.enter().append("circle")
.attr("transform", function(d) { return "translate(" + d + ")"; })
.attr("r", 2)
text1 = svg.append("svg:text")
.text("control")
.attr("x", 150)
.attr("y", 200)
.style("stroke", "orange")
.style("stroke-width", 0)
.style("font-size", "80%")
.style("fill", "orange");
});
The points are drawn correctly but the polygons of the tesselation are not.
I have tried to add header row and the csv.parse() function without success. At the beginning I was thinking the solution was to iterate over the array to parse to float, but I couldn't do it. If that is the reason why the points are drawn correctly anyway?.
The csv file looks like this:
0.0,0.0
116.78032769067718,0.0
193.02842412648215,78.92418723196411
323.01058809711515,54.15210221124609
378.8576448450217,202.5192012545716
...

I think it is, as you suggest, a problem with the numbers getting parsed as Strings rather than Numbers. Even if that's not what's breaking it, it'd be good to fix. This is one way of doing it (might be a more idiomatic way to do it, dunno):
var vertices = d3.csv.parseRows(
datasetText,
function(pt) { return [parseFloat(pt[0]), parseFloat(pt[1])]; })
);
That might fix your problem.

Related

D3.js Heatmap: How to read from a nested json and create a Heatmap using D3

I have been trying for a couple of days now to read a JSON file with data and generate a heatmap from it. The JSON contains the frequency of git commits by day, for every week for 52 weeks.The data in the JSON (frequency each day) is nested hence I do not know how to extract the data and get the code to represent it. I am really new to do and I do not understand what I am missing or doing wrong. Would really appreciate if anyone can help me out. I read several links, but none of them deals with JSON and I am also having trouble understanding it. I would like to create 52 rows representing a week and 7 columns for each day of the week which I already have. The rectangles just dont reflect the frequency. I am not sure how to do that. A sample of the data I have and the code I have done so far is as such:
Code:
<script type='text/javascript' src='http://d3js.org/d3.v4.min.js'></script>
<script>
var url = "data/git-commit-frequency.json"
var color = ["#ffffd9","#c7e9b4","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"]
d3.json(url, function (data) {
let gridSize = 50;
let width = (gridSize + 1) * 7;
let height = (gridSize + 1) * data.length;
// define range!!!
var x = d3.scaleLinear()
.domain([0, 7])
.range([0, width]);
//52 weeks
var y = d3.scaleLinear()
.domain([0, data.length])
.rangeRound([height, 0]);
let svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
// Generate rows for each json object
var rows = svg.selectAll('.row')
.data(data)
.enter()
.append('g')
.attr("transform", function(d, i){
return "translate(0," + y(i) + ")"
})
// Generate rects for the array of days per object
let box = rows.selectAll("rect")
.data(function(d){ return d.days })
.enter()
.append("rect")
.attr("x", function(d, i) { return x(i); })
.attr("width", gridSize)
.attr("height", gridSize)
.style("fill", 'black')
.attr("class", "bordered");
})
</script>
</body>
JSON:
[
{
"days": [
0,
0,
0,
0,
0,
1,
0
],
"total": 1,
"week": 1457827200
}
]
I have 52 of such JSON objects in the file.
Your code attaches values to the rects with this piece of code, and specifically the .data(function(d){ return d.days })
let box = rows.selectAll("rect")
.data(function(d){ return d.days })
.enter()
.append("rect")
.attr("x", function(d, i) { return x(i); })
.attr("width", gridSize)
.attr("height", gridSize)
.style("fill", 'black')
.attr("class", "bordered");
})
Each appended rect will have one of the values from the days array (eg 0, or 1).
You can inspect this in the browser console (right click on the rect, select Inspect and go to Properties > Rect > data and you will see what values have been appended to that element.
From there, you can typically access the value using an anonymous function within setting the style or attr of your rect. For example, to set colour:
.style("fill", function(d) { return d === 0 ? "grey" : "black"; })
which sets rects with 0 value to be grey, and anything else black. Although you as you get more complex mappings, using d3-scale becomes valuable.

How to append different shapes including images depending on data in D3.js?

I tried to solve this by looking up several similar questions, but none could solve it. I want to create a force-directed graph using this form of data out of a JSON-File:
{
"nodes":[
{"name":"A", "imagelink": "url-to-img" ,"shape-type":circle},
{"name":"B", "imagelink": "url-to-img" , "shape-type":rectangle},
{"name":"A", "imagelink": "url-to-img" ,"shape-type":triangle}
]
"links":[
{"source":1,"target":0,"value":1},
{"source":2,"target":1,"value":1},
{"source":2,"target":0,"value":1}
]
}
The graph's nodes should be a group consisting of a geometrical shape (defined by "shape-type") including an image (defined by "imagelink").
This is the basic force-layout code which I used:
var width = 960,
height = 500;
var force = d3.layout.force()
.charge(-1000)
.linkDistance(150)
.size([width, height]);
var svg = d3.select("body").append("svg");
d3.json("json-file.json");
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function (d) {
return Math.sqrt(d.value);
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
For including an image into a shape I used the "clip-path method" (example for circle):
node.append("circle")
.attr("r", 20)
.attr("cy", 0)
.attr("cx", 0)
.style("fill", "white")
.style("stroke", "black")
.style("stroke-width", "2");
var clippath = node.append('clipPath').attr(function (d) {
return ("clip-" + convert_shape(d.group))
});
clippath.append("circle")
.attr("r", 19)
.attr("cy", 0)
.attr("cx", 0);
node.append("image")
.attr("x", -37.5)
.attr("y", -37.5)
.attr("height", 75)
.attr("width", 75)
.attr("xlink:href", function (d) {return (d.imagelink)})
.attr("clip-path", function (d) {return ("url(#clip-" + d.shapetype) + ")")
});
As you can see, my problem is to make the append()-function data-depended. So how can I realize this?
Thanks.
Here is a working fiddle for drawing a force-directed graph with different shapes and images.
I used the pattern solution to include the image into shapes.

Do I need to nest my data to have a key (d3 csv reusable chart)?

I seem to be having some key issues with an enter,update, exit chart. My data() function is having all kinds of trouble tracking the insertion and removal of data, and the scaling and removing and appending are not behaving properly.
I've made a Plunkr.
I am trying to make the chart update to display different data by year. The year divs can be clicked.
Do I need to nest my data? Is there a better solution?
Alternatively, is the best practice to create a separate update() function or to re-run the chartdraw() function?
Please excuse the janky code. Still figuring this out :)
it doesn't look like you have an update for either the barsBars or barsText. Changes are only triggered on enter and exit.
Try adding this between the 'barsBars = svg.selectAll...' and 'barsBars.enter()...' lines:
barsBars
.transition()
.attr("x", function(d, i) {
return xScale(d.funded_month) + marginleft;
})
.attr("y", function(d) {
return h - yScale(d.value);
})
.attr("width", barwidth - .2)
.attr("height", function(d) {
return yScale(d.value);
})
.attr("fill", function(d) {
var picker = d.value / 138579040 + 100;
picker = parseInt(picker)
barColor = "rgb(40, 80, " + picker + ")";
return barColor;
});
And this between 'barsText = svg.selectAll...' and 'barsText.enter()...' lines:
barsText
.transition()
.attr("x", function(d, i) {
return xScale(d.funded_month) + marginleft + xAdjust;
//return xScale(i) + barwidth / 2;
})
.attr("y", function(d) {
return h - yScale(d.value) - 2;
});
The .transition() lines are not necessary but they change how the graph updates. Try commenting them out to see the difference.
See Mike Bostock's general update patterns examples for more:
http://bl.ocks.org/mbostock/3808234

max and min values from json in d3

I'm following a tutorial of d3 that uses tsv rather than json.
I'm more familiar with json so I wanted to apply my own json object to the example.
I found this and tried to apply it but i'm still getting errors:
How to use d3.min and d3.max within a d3.json command
Here is my code. I am getting the json object back with my first log. And the correct length with my second log.
Yet I get this error when trying to use d.value in d3.max()
Error: Invalid value for attribute width="NaN"
d3.json("sampledata.json", function(error, data) {
var maxx = d3.entries(data)
.sort(function(a,b){return d3.descending(a.value, b.value);})
console.log(maxx)
console.log(data.length);
mydata = data;
x.domain([0, d3.max(data, function(d) { return d.value; })])
chart.attr("height", barHeight * mydata.length);
var bar = chart.selectAll("g")
.data(mydata)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
// WHERE MY ERROR IS because there is no value for x
bar.append("rect")
.attr("width", x)
.attr("height", barHeight - 1);
bar.append("text")
.attr("x", function(d) { return x(d) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d) { return d; });
});
I think what you need to do, since x is a scale of some kind, is the following:
bar.append("rect")
.attr("width", function(d) {
return x(d.value);
})
.attr("height", barHeight - 1);
Otherwise it is just going to pass d into x, which isn't going to help you.

Code works with array, but not with csv-file in d3.js. Why?

I got a problem with using an csv-file in d3.js.
At first, I got my visualization by using an array with data:
var dataset = [1,1,1,1,1,1,1,2,3,4]
Depending on the number, there will be a different color for a rectangle. I did this by using the following lines of code:
.style("fill", function(d) {
if (d==1) {return "black"}
else if (d==2) {return "red"}
else if (d==3) {return "yellow"}
else if (d==4) {return "green"}
else {return "purple"}
;})
Because I want to make my code more flexible, I want to do the same using an csv-datafile. It looks like this:
2008
1
1
1
1
2
3
4
I included it with the following line of code:
d3.csv("landtag.csv", function(d) {
}
But, it doesn't work: All rectangle are purple, therefore the "else" was chosen.
The number of circles depends on the datalength - this is working!
Why is that possible?
Do I insert the csv-file in the wrong way?
I tried already some solutions in tutorials, but none worked.
Thanks for your help!
My complete code:
// Width and height
var w = 1000;
var h = 1000;
// create svg variable
var svg = d3.select("p")
.append("svg")
.attr("id", "sitze") ;
var sitze;
d3.csv("landtag.csv", function(d) {
// create variable
var rect = svg.selectAll("rect")
.data(d["2008"])
.enter()
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 50)
.attr("height", 50)
//Bar width depending on number of datapoints in array
.attr("x", function(d, i) {
return i * (w / d.length);
})
// if for different colors
.style("fill", function(d) {
if (d=="1") {return "black"}
else if (d=="2") {return "red"}
else if (d=="3") {return "yellow"}
else if (d=="4") {return "green"}
else {return "purple"}
;})
// paddings
.attr("x", function(d, i) {return (i % 17) * 55;})
.attr("y", function(d, i) {return Math.floor(i / 17) * 55;});
})
The contents of your CSV file are parsed as strings. This means that you don't get the number 1, but the string "1". If you change all your comparisons to strings (e.g. (d=="1")), it should work again. Alternatively, you can convert the strings into numbers before processing them, e.g. with this code.
csv.forEach(function(d) { return +d; });