I am learning D3 on the fly today. Below is a sample set of code with a JSON data set. I am currently trying to create a second graph next to the main donut chart. This chart will be another donut chart of the thin_vols[] array which is nested in each object. If you see the commented code below, I am attempting to build the second chart with no such luck. Any advice or help is greatly appreciated. Thank you.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>D3 Donut Chart</title>
</head>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.arc path {
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var sampleSet = {
"title":"DummyData",
"data":[
{
"origin":"Disk_Pool_1",
"volumeName":"Vol1",
"usage":15,
"type":"thick",
"thin_vols":[]
},
{
"origin":"Disk_Pool_1",
"volumeName":"Vol2",
"usage":25,
"type":"thick",
"thin_vols":[]
},
{
"origin":"Disk_Pool_1",
"volumeName":"Repo_06",
"usage":50,
"type":"thick",
"thin_vols":[
{
"origin":"Repo_06",
"volumeName":"thinVol1",
"usage":10,
"max":20,
"type":"thin"
},
{
"origin":"Repo_06",
"volumeName":"thinVol2",
"usage":10,
"max":30,
"type":"thin"
},
{
"origin":"Repo_06",
"volumeName":"freespace",
"usage":20,
"max":40,
"type":"freespace"
}]
}
]
};
var m = 10,
r = 100,
z = d3.scale.category20c();
var pie = d3.layout.pie()
.value(function(d) { return +d.usage; })
.sort(function(a, b) { return b.usage - a.usage; });
var arc = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r);
//here
var disks = d3.nest()
.key(function(d) { return d.origin; })
.entries(sampleSet.data);
var svg = d3.select("body").selectAll("div")
.data(disks)
.enter().append("div")
.style("display", "inline-block")
.style("width", (r + m) * 2 + "px")
.style("height", (r + m) * 2 + "px")
.append("svg:svg")
.attr("width", (r + m) * 2)
.attr("height", (r + m) * 2)
.append("svg:g")
.attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");
svg.append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.key; });
var g = svg.selectAll("g")
.data(function(d) { return pie(d.values); })
.enter().append("svg:g");
g.append("svg:path")
.attr("d", arc)
.style("fill", function(d) { return z(d.data.volumeName); })
.append("svg:title")
.text(function(d) { return d.data.volumeName + ": " + d.data.usage; });
g.filter(function(d) { return d.endAngle - d.startAngle > .2; }).append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")rotate(" + angle(d) + ")"; })
.text(function(d) { return d.data.volumeName + ": " + d.data.usage; });
var disks2 = d3.nest()
.key(function(d) { return d.origin; })
.entries(sampleSet.data.thin_vols);
var svg2 = d3.select("body").selectAll("div")
.data(disks2)
.enter().append("div")
.style("display", "inline-block")
.style("width", (r + m) * 2 + "px")
.style("height", (r + m) * 2 + "px")
.append("svg:svg")
.attr("width", (r + m) * 2)
.attr("height", (r + m) * 2)
.append("svg:g")
.attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");
svg2.append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.key; });
var g2 = svg2.selectAll("g")
.data(function(d) { return pie(d.values); })
.enter().append("svg:g");
g2.append("svg:path")
.attr("d", arc)
.style("fill", function(d) { return z(d.data.volumeName); })
.append("svg:title")
.text(function(d) { return d.data.thinvolumeName + ": " + d.data.usage; });
g2.filter(function(d) { return d.endAngle - d.startAngle > .2; }).append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")rotate(" + angle(d) + ")"; })
.text(function(d) { return d.data.volumeName + ": " + d.data.usage; });
function angle(d) {
var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
return a > 90 ? a - 180 : a;
}
</script>
</body>
</html>
I was able to render the second chart by flattening the existing JSON. See the below source code.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>D3 Donut Chart</title>
</head>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.arc path {
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var sampleSet = {
"title": "DummyData",
"data": [
{
"origin": "Disk_Pool_1",
"volumeName": "Vol1",
"usage": 15,
"type": "thick"
},
{
"origin": "Disk_Pool_1",
"volumeName": "Vol2",
"usage": 25,
"type": "thick"
},
{
"origin": "Disk_Pool_1",
"volumeName": "Repo_06",
"usage": 50,
"type": "thick"
},
{
"origin": "Repo_06",
"volumeName": "thinVol1",
"usage": 10,
"max": 20,
"type": "thin"
},
{
"origin": "Repo_06",
"volumeName": "thinVol2",
"usage": 10,
"max": 30,
"type": "thin"
},
{
"origin": "Repo_06",
"volumeName": "freespace",
"usage": 20,
"max": 40,
"type": "freespace"
}
]
};
var m = 10, r = 100, z = d3.scale.category20c();
var pie = d3.layout.pie()
.value(function (d) {
return +d.usage;
})
.sort(function (a, b) {
return b.usage - a.usage;
});
var arc = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r);
var disks = d3.nest()
.key(function (d) {
return d.origin;
})
.entries(sampleSet.data);
var svg = d3.select("body").selectAll("div")
.data(disks)
.enter().append("div")
.style("display", "inline-block")
.style("width", (r + m) * 2 + "px")
.style("height", (r + m) * 2 + "px")
.append("svg:svg")
.attr("width", (r + m) * 2)
.attr("height", (r + m) * 2)
.append("svg:g")
.attr("transform", "translate(" + (r + m) + "," + (r + m) + ")");
svg.append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function (d) {
return d.key;
});
var g = svg.selectAll("g")
.data(function (d) {
return pie(d.values);
})
.enter().append("svg:g");
g.append("svg:path")
.attr("d", arc)
.style("fill", function (d) {
return z(d.data.volumeName);
})
.append("svg:title")
.text(function (d) {
return d.data.volumeName + ": " + d.data.usage;
});
g.filter(function (d) {
return d.endAngle - d.startAngle > .2;
}).append("svg:text")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("transform", function (d) {
return "translate(" + arc.centroid(d) + ")rotate(" + angle(d) + ")";
})
.text(function (d) {
return d.data.volumeName + ": " + d.data.usage;
});
function angle(d) {
var a = (d.startAngle + d.endAngle) * 90 / Math.PI - 90;
return a > 90 ? a - 180 : a;
}
</script>
</body>
</html>
Related
I'm trying to build a tree using d3js. I'm having a 2 sided tree with a root in the center. I am showing parents towards the right of the root and showing children towards the left of the root. here is my code.
var data = {
"name": "Root",
"img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png",
"children": [{
"name": "3",
"img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
}, {
"name": "4",
"img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
}],
"parent": [{
"name": "1",
"img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
}, {
"name": "2",
"img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
}]
};
var bgColors = ['#fd90b5', '#6ca1e9', '#fa975c', '#eb7092', '#f88962', '#a094ed', '#7f8de1'];
var dr = 0;
// Left data
var data1 = {
"name": data.name,
"children": JSON.parse(JSON.stringify(data.children))
};
// Right data
var data2 = {
"name": data.name,
"children": JSON.parse(JSON.stringify(data.parent))
};
// Create d3 hierarchies
var right = d3.hierarchy(data1);
var left = d3.hierarchy(data2);
// Render both trees
drawTree(right, "right")
drawTree(left, "left")
// draw single tree
function drawTree(root, pos) {
var refType;
if (pos == 'left')
refType = 'left';
else
refType = 'right';
var SWITCH_CONST = 1;
if (pos === "left") {
SWITCH_CONST = -1;
}
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height")
var g = svg.append("g").attr("transform", "translate(" + width / 2 + ",0)");
var tree = d3.tree()
.size([height, SWITCH_CONST * (width - 150) / 2]);
tree(root)
var nodes = root.descendants();
var links = root.links();
nodes[0].x = height / 2
// Create links
var link = g.selectAll(".link")
.data(links)
.enter()
link.append("path")
.attr("class", "link")
.attr("d", function (d) {
//first return returns a curve and the second will return straight lines in
//return "M" + d.target.y + "," + d.target.x + "C" + (d.target.y + d.source.y) / 2.5 + "," + d.target.x + " " + (d.target.y + d.source.y) / 2 + "," + d.source.x + " " + d.source.y + "," + d.source.x;
return "M" + d.target.y + "," + d.target.x + "A" + dr + "," + dr + " 1,0 0 " + d.source.y + "," + d.source.x;
});
link.append("text")
.attr("font-family", "Arial, Helvetica, sans-serif")
.attr("fill", "Black")
.style("font", "normal 12px Arial")
.attr("transform", function (d) {
return "translate(" +
((d.source.y + d.target.y) / 2) + "," +
((d.source.x + d.target.x) / 2) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.data(nodes)
.text(refType);
// Create nodes
var node = g.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", function (d) {
return "node" + (d.children ? " node--internal" : " node--leaf");
})
.attr("transform", function (d) {
return "translate(" + d.y + "," + d.x + ")";
})
node.append('circle')
.attr('class', 'icon-wrap')
.attr('x', 0)
.attr('y', 0)
.attr('r', 25)
.style('fill', 'black');
node.append('image')
.attr('href', d => d.data.img)
.attr('x', '-25')
.attr('y', '-25')
.attr('height', '50')
.attr('width', '50');
node.append("text")
.attr("dy", 45)
.style("text-anchor", "middle")
.text(d => d.data.name);
}
.node circle {
fill: #999;
}
.node text {
font: 12px sans-serif;
}
.node--internal circle {
fill: #555;
}
.link {
fill: none;
stroke: #555;
stroke-opacity: 0.4;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg width="800" height="550"></svg>
Here my issue is that I'm unable to get the logo seen in the root node.
Thanks
Despite the img property existing in the data object, it is missing in the two objects you're actually passing to d3.hierarchy(), which are data1 and data2. Therefore, it should be:
var data1 = {
"name": data.name,
"img": data.img,
"children": JSON.parse(JSON.stringify(data.children))
};
The same for data2. Here is your code with those 2 changes:
var data = {
"name": "Root",
"img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png",
"children": [{
"name": "3",
"img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
}, {
"name": "4",
"img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
}],
"parent": [{
"name": "1",
"img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
}, {
"name": "2",
"img": "https://www.freelogodesign.org/Content/img/logo-samples/flooop.png"
}]
};
var bgColors = ['#fd90b5', '#6ca1e9', '#fa975c', '#eb7092', '#f88962', '#a094ed', '#7f8de1'];
var dr = 0;
// Left data
var data1 = {
"name": data.name,
"img": data.img,
"children": JSON.parse(JSON.stringify(data.children))
};
// Right data
var data2 = {
"name": data.name,
"img": data.img,
"children": JSON.parse(JSON.stringify(data.parent))
};
// Create d3 hierarchies
var right = d3.hierarchy(data1);
var left = d3.hierarchy(data2);
// Render both trees
drawTree(right, "right")
drawTree(left, "left")
// draw single tree
function drawTree(root, pos) {
var refType;
if (pos == 'left')
refType = 'left';
else
refType = 'right';
var SWITCH_CONST = 1;
if (pos === "left") {
SWITCH_CONST = -1;
}
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height")
var g = svg.append("g").attr("transform", "translate(" + width / 2 + ",0)");
var tree = d3.tree()
.size([height, SWITCH_CONST * (width - 150) / 2]);
tree(root)
var nodes = root.descendants();
var links = root.links();
nodes[0].x = height / 2
// Create links
var link = g.selectAll(".link")
.data(links)
.enter()
link.append("path")
.attr("class", "link")
.attr("d", function(d) {
//first return returns a curve and the second will return straight lines in
//return "M" + d.target.y + "," + d.target.x + "C" + (d.target.y + d.source.y) / 2.5 + "," + d.target.x + " " + (d.target.y + d.source.y) / 2 + "," + d.source.x + " " + d.source.y + "," + d.source.x;
return "M" + d.target.y + "," + d.target.x + "A" + dr + "," + dr + " 1,0 0 " + d.source.y + "," + d.source.x;
});
link.append("text")
.attr("font-family", "Arial, Helvetica, sans-serif")
.attr("fill", "Black")
.style("font", "normal 12px Arial")
.attr("transform", function(d) {
return "translate(" +
((d.source.y + d.target.y) / 2) + "," +
((d.source.x + d.target.x) / 2) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.data(nodes)
.text(refType);
// Create nodes
var node = g.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.attr("class", function(d) {
return "node" + (d.children ? " node--internal" : " node--leaf");
})
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
})
node.append('circle')
.attr('class', 'icon-wrap')
.attr('x', 0)
.attr('y', 0)
.attr('r', 25)
.style('fill', 'black');
node.append('image')
.attr('href', d => d.data.img)
.attr('x', '-25')
.attr('y', '-25')
.attr('height', '50')
.attr('width', '50');
node.append("text")
.attr("dy", 45)
.style("text-anchor", "middle")
.text(d => d.data.name);
}
.node circle {
fill: #999;
}
.node text {
font: 12px sans-serif;
}
.node--internal circle {
fill: #555;
}
.link {
fill: none;
stroke: #555;
stroke-opacity: 0.4;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg width="800" height="550"></svg>
I'm new to d3 charts and javascript, and this has been an uphill battle.
After a bunch a research, I was able to populate the chart with a CSV file. So now, I'm trying to populate the chart with json data.
This is my code. It's loosely based on this example. But I prefer using my code (ie. d3.v4):
var width = 960,
height = 136,
cellSize = 17;
var color = d3.scaleQuantize()
.domain([9000, 12000])
.range(["Blue", "Red", "Green", "Yellow", "Purple", "Black"]);
var dateParse = d3.timeFormat("%Y-%m-%d");
var svg = d3.select("body")
.selectAll("svg")
.data(d3.range(2017, 2018))
.enter().append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")");
svg.append("text")
.attr("transform", "translate(-6," + cellSize * 3.5 + ")rotate(-90)")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "middle")
.text(function(d) {
return d;
});
var rect = svg.append("g")
.attr("fill", "none")
.attr("stroke", "#ccc")
.selectAll("rect")
.data(function(d) {
return d3.timeDays(new Date(d, 0, 1), new Date(d + 1, 0, 1));
})
.enter().append("rect")
.attr("width", cellSize)
.attr("height", cellSize)
.attr("x", function(d) {
return d3.timeWeek.count(d3.timeYear(d), d) * cellSize;
})
.attr("y", function(d) {
return d.getDay() * cellSize;
})
.datum(d3.timeFormat("%Y-%m-%d"));
svg.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.selectAll("path")
.data(function(d) {
return d3.timeMonths(new Date(d, 0, 1), new Date(d + 1, 0, 1));
})
.enter().append("path")
.attr("d", pathMonth);
d3.json("data3.json", function(error, data) {
//populating data since i don't have the file
data = [{
"date": "2017-01-04",
"open": 10430.69
}, {
"date": "2017-01-05",
"open": 10584.56
}];
data.forEach(function(d) {
d.dd = dateParse(new Date(d.date));
console.log(d.dd);
});
var nest = d3.nest()
.key(function(d) {
return d.dd;
})
.map(data);
rect.filter(function(d) {
return d in data;
})
.attr("fill", function(d) {
return color(data[d]);
})
.append("title")
.text(function(d) {
return d + ": " + data[d];
});
});
function pathMonth(t0) {
var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
d0 = t0.getDay(),
w0 = d3.timeWeek.count(d3.timeYear(t0), t0),
d1 = t1.getDay(),
w1 = d3.timeWeek.count(d3.timeYear(t1), t1);
return "M" + (w0 + 1) * cellSize + "," + d0 * cellSize +
"H" + w0 * cellSize + "V" + 7 * cellSize +
"H" + w1 * cellSize + "V" + (d1 + 1) * cellSize +
"H" + (w1 + 1) * cellSize + "V" + 0 +
"H" + (w0 + 1) * cellSize + "Z";
}
<script src="http://d3js.org/d3.v4.min.js"></script>
There are a few changes needed for your code to work. These are mostly related to the use of data instead of nest, and a minor change (for which I cannot find information on) in d3 v4 as compared with d3 v3.
Filter
Firstly, you are not filtering your data correctly:
You do not want to filter like this:
return d in data;
The in operator is for properties of an object, data is an array.
You want to filter by your nest (as in the example):
return d in nest;
Secondly, at least in my brief testing, d3.nest behaves slightly differently in d3 v4 (this might be dependent on version, I've used 4.9.1 (min) in the snippet below). When using keys that begin with numbers, d3 seems to be appending a dollar sign at the beginning of each key when using d3.nest:
D3v4 example:
data = [{
"date": "2017-01-04", "open": 10430.69
}, {
"date": "2017-01-05", "open": 10584.56
}];
var nest = d3.nest()
.key(function(d) { return d.date; })
.map(data);
console.log(nest);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
Compared with D3v3:
data = [{
"date": "2017-01-04", "open": 10430.69
}, {
"date": "2017-01-05", "open": 10584.56
}];
var nest = d3.nest()
.key(function(d) { return d.date; })
.map(data);
console.log(nest);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
If you are seeing this behaviour, you'll need to change your filter accordingly:
return ("$" + d) in nest;
Accessing Nest Properties
Thirdly, as data is just an array, data[d] is not likely to get desired results as d will be a date string, you need to access the nest object. Logging nest might help in finding the proper properties. Instead of:
return color(data[d]);
Try:
return color(nest[("$" + d)][0].open);
Which is very similar to the linked example in the question (with the exception of that dollar sign thing again).
Optimization
Related to your other recent question, this code
var date = "2017-01-02";
var dateParse = d3.timeFormat("%Y-%m-%d");
console.log(dateParse(new Date(date)));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
does nothing. It takes a string representing a date and converts it to a date object, then converts it back into the same string representation you started with. You can drop this portion of the code, it was used in the linked example because it was converting from a m/d/Y date string to a date object, and then to a Y-m-d date string. Your initial date format is already in the format you want, so there is no need to modify it. Use just:
var nest = d3.nest()
.key(function(d) {
return d.date;
})
.map(data);
Rather than:
data.forEach(function(d) {
d.dd = dateParse(d.date);
});
var nest = d3.nest()
.key(function(d) {
return d.dd;
})
.map(data);
Result
These changes (I've stripped out the text to make the example simpler, removed the external file reference, etc) result in:
var width = 960,
height = 136,
cellSize = 17;
var color = d3.scaleQuantize()
.domain([9000, 12000])
.range(["Blue", "Red", "Green", "Yellow", "Purple", "Black"]);
var dateFormat = d3.timeFormat("%Y-%m-%d");
var svg = d3.select("body")
.selectAll("svg")
.data(d3.range(2017, 2018))
.enter().append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")");
var rect = svg.append("g")
.attr("fill", "none")
.attr("stroke", "#ccc")
.selectAll("rect")
.data(function(d) {
return d3.timeDays(new Date(d, 0, 1), new Date(d + 1, 0, 1));
})
.enter().append("rect")
.attr("width", cellSize)
.attr("height", cellSize)
.attr("x", function(d) {
return d3.timeWeek.count(d3.timeYear(d), d) * cellSize;
})
.attr("y", function(d) {
return d.getDay() * cellSize;
})
.datum(dateFormat);
svg.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.selectAll("path")
.data(function(d) {
return d3.timeMonths(new Date(d, 0, 1), new Date(d + 1, 0, 1));
})
.enter().append("path")
.attr("d", pathMonth);
data = [{
"date": "2017-01-04",
"open": 10430.69
}, {
"date": "2017-01-05",
"open": 10584.56
}];
var nest = d3.nest()
.key(function(d) {
return d.date;
})
.map(data);
rect.filter(function(d) {
return ("$" + d) in nest;
})
.attr("fill", function(d) {
return color(nest[("$" + d)][0].open);
})
function pathMonth(t0) {
var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
d0 = t0.getDay(),
w0 = d3.timeWeek.count(d3.timeYear(t0), t0),
d1 = t1.getDay(),
w1 = d3.timeWeek.count(d3.timeYear(t1), t1);
return "M" + (w0 + 1) * cellSize + "," + d0 * cellSize +
"H" + w0 * cellSize + "V" + 7 * cellSize +
"H" + w1 * cellSize + "V" + (d1 + 1) * cellSize +
"H" + (w1 + 1) * cellSize + "V" + 0 +
"H" + (w0 + 1) * cellSize + "Z";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
Looks like you were forgetting to declare dateParse (and using it wrong).
var dateParse = d3.timeParse("%Y-%m-%d");
var width = 960,
height = 136,
cellSize = 17;
var color = d3.scaleQuantize()
.domain([9000, 12000])
.range(["Blue", "Red", "Green", "Yellow", "Purple", "Black"]);
var dateParse = d3.timeParse("%Y-%m-%d");
var svg = d3.select("body")
.selectAll("svg")
.data(d3.range(2017, 2018))
.enter().append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")");
svg.append("text")
.attr("transform", "translate(-6," + cellSize * 3.5 + ")rotate(-90)")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "middle")
.text(function(d) {
return d;
});
var rect = svg.append("g")
.attr("fill", "none")
.attr("stroke", "#ccc")
.selectAll("rect")
.data(function(d) {
return d3.timeDays(new Date(d, 0, 1), new Date(d + 1, 0, 1));
})
.enter().append("rect")
.attr("width", cellSize)
.attr("height", cellSize)
.attr("x", function(d) {
return d3.timeWeek.count(d3.timeYear(d), d) * cellSize;
})
.attr("y", function(d) {
return d.getDay() * cellSize;
})
.datum(d3.timeFormat("%Y-%m-%d"));
svg.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.selectAll("path")
.data(function(d) {
return d3.timeMonths(new Date(d, 0, 1), new Date(d + 1, 0, 1));
})
.enter().append("path")
.attr("d", pathMonth);
d3.json("data3.json", function(error, data) {
//populating data since i don't have the file
data = [{
"date": "2017-01-04",
"open": 10430.69
}, {
"date": "2017-01-05",
"open": 10584.56
}];
data.forEach(function(d) {
d.dd = dateParse(d.date);
console.log(d.dd);
});
var nest = d3.nest()
.key(function(d) {
return d.dd;
})
.map(data);
rect.filter(function(d) {
return d in data;
})
.attr("fill", function(d) {
return color(data[d]);
})
.append("title")
.text(function(d) {
return d + ": " + data[d];
});
});
function pathMonth(t0) {
var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0),
d0 = t0.getDay(),
w0 = d3.timeWeek.count(d3.timeYear(t0), t0),
d1 = t1.getDay(),
w1 = d3.timeWeek.count(d3.timeYear(t1), t1);
return "M" + (w0 + 1) * cellSize + "," + d0 * cellSize +
"H" + w0 * cellSize + "V" + 7 * cellSize +
"H" + w1 * cellSize + "V" + (d1 + 1) * cellSize +
"H" + (w1 + 1) * cellSize + "V" + 0 +
"H" + (w0 + 1) * cellSize + "Z";
}
<script src="http://d3js.org/d3.v4.min.js"></script>
I'm trying to build a legend to a calendar view graph using d3 js like this one
but i'm trying to build a legend to it like the kind of shown in the pic
but i'm not able to generate this
here's my code to generated calender view map
var Comparison_Type_Max = d3.max(inputData, function(d) { return d.value; });
var data = d3.nest()
.key(function(d) { return d.Date; })
.rollup(function(d) { return (d[0].value ); })
.map(inputData);
var margin = { top: 20, right: 50, bottom: 10, left: 60 },
width = $(dom_element_to_append_to).parent().width() - margin.left - margin.right,
cellSize = Math.floor(width/52),
height = cellSize * 7 + margin.top + margin.bottom ,
week_days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
month = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
$rootScope.defaultWidth = width;
$rootScope.cellSize = cellSize;
var day = d3.time.format("%w"),
week = d3.time.format("%U"),
percent = d3.format(".1%"),
format = d3.time.format("%Y%m%d"),
legendElementWidth = cellSize * 2,
buckets = 9,
parseDate = d3.time.format("%Y%m%d").parse;
var color = d3.scale.linear().range(colorRange)
.domain([0, 1]);
var colorScale = d3.scale.quantile()
.domain([0, buckets - 1, d3.max(data, function (d) { return d.value; })])
.range(colorRange);
var svg = d3.select(dom_element_to_append_to).selectAll("svg")
.data(d3.range(year, year+1))
.enter().append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("data-height", '0.5678')
.attr("class", "RdYlGn")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//.attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")");
console.log("1");
svg.append("text")
.attr("transform", "translate(-38," + cellSize * 3.5 + ")rotate(-90)")
.style("text-anchor", "middle")
.text(function(d) { return d; });
for (var i=0; i<7; i++)
{
svg.append("text")
.attr("transform", "translate(-5," + cellSize*(i+1) + ")")
.style("text-anchor", "end")
.attr("dy", "-.25em")
.text(function(d) { return week_days[i]; });
}
var rect = svg.selectAll(".day")
.data(function(d) { return d3.time.days(new Date(d, 0, 1), new Date(d + 1, 0, 1)); })
.enter()
.append("rect")
.attr("class", "day")
.attr("width", cellSize)
.attr("height", cellSize)
.attr("x", function(d) { return week(d) * cellSize; })
.attr("y", function(d) { return day(d) * cellSize; })
.attr("fill",'#fff')
.datum(format);
console.log("2");
/* var legend = svg.selectAll(".legend")
.data(data)
.enter().append("g")
.attr("class", "legend");
//.attr("transform", function(d, i) { return "translate(" + (((i+1) * cellSize*4.33)) + ",0)"; });
legend.append("rect")
.attr("x", function(d, i) { return legendElementWidth * i; })
.attr("y", height)
.attr("width", legendElementWidth)
.attr("height", cellSize / 2)
.style("fill", function(d, i) { return color(Math.sqrt(data[d] / Comparison_Type_Max )); });
legend.append("text")
.attr("class", "mono")
.text(function(d) { return "≥ " + Math.round(d); })
.attr("x", function(d, i) { return legendElementWidth * i; })
.attr("y", height + cellSize);
*/
var legend = svg.selectAll(".legend")
.data([0].concat(colorScale.quantiles()), function(d) { return d; });
legend.enter().append("g")
.attr("class", "legend");
legend.append("rect")
.attr("x", function(d, i) { return legendElementWidth * i; })
.attr("y", height)
.attr("width", legendElementWidth)
.attr("height", cellSize / 2)
.style("fill", function(d, i) { return color(Math.sqrt(data[i] / Comparison_Type_Max )); });
legend.append("text")
.attr("class", "mono")
.text(function(d) { return "≥ " + Math.round(d); })
.attr("x", function(d, i) { return legendElementWidth * i; })
.attr("y", height + cellSize);
legend.exit().remove();
svg.selectAll(".month")
.data(function(d) { return d3.time.months(new Date(d, 0, 1), new Date(d + 1, 0, 1)); })
.enter().append("path")
.attr("class", "month")
.attr("id", function(d,i){ return month[i] + "" + varID })
.attr("d", monthPath);
console.log("4");
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
var year = d/10000;
year = Math.floor(year);
var month = d/100;
month = Math.floor(month % 100);
var day = d % 100;
if(day<10) day = "0" + day;
if(month < 10) month = "0" + month;
if(isCurrency)
return "<div><span>Date:</span> <span style='color:white'>" + day + "/" + month + "/" + year + "</span></div>" +
"<div><span>Value:</span> <span style='color:white'><span>₹</span>" + d3.format(",")(data[d]) + "</span></div>";
else
return "<div><span>Date:</span> <span style='color:white'>" + day + "/" + month + "/" + year + "</span></div>" +
"<div><span>Value:</span> <span style='color:white'>" + d3.format(",")(data[d]) + "</span></div>";
});
console.log("5");
svg.call(tip);
console.log("6");
rect.filter(function(d) { return d in data; })
.on("click", function(d){
console.log(d);
var year = d/10000;
year = Math.floor(year);
var monthInt = d/100;
console.log(dom_element_to_append_to);
var val,id;
if(dom_element_to_append_to=='.sms-yearheatmap-container-negative')
val = 0;
else if (dom_element_to_append_to=='.sms-yearheatmap-container-positive')
val = 1;
else val = 2;
monthInt = Math.floor(monthInt % 100);
for (var itr = 0; itr<12; ++itr) {
id = month[itr] + "" + varID;
$('#' + id).css("z-index",0);
$('#' + id).css("stroke","#000");
$('#' + id).css("stroke-width", "2.5px");
}
id = month[monthInt-1] + "" + varID;
$('#' + id).css("stroke","blue");
$('#' + id).css("position","relative");
$('#' + id).css("z-index",1000);
$('#' + id).css("stroke-width", "4.5px");
console.log("year " + year + " month" + monthInt);
$rootScope.loadDayHourHeatGraph(year, monthInt , val, isCurrency);
})
.attr("fill", function(d) { return color(Math.sqrt(data[d] / Comparison_Type_Max )); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
/*.attr("data-title", function(d) { return "value : "+Math.round(data[d])});
$("rect").tooltip({container: 'body', html: true, placement:'top'}); */
/* $("rect").on("click", function(d) {
console.log(d);
});*/
function numberWithCommas(x) {
x = x.toString();
var pattern = /(-?\d+)(\d{3})/;
while (pattern.test(x))
x = x.replace(pattern, "$1,$2");
return x;
it is generting the graph correctly but my legend is not generating with it
here's the code of legend which i wrote above
var legend = svg.selectAll(".legend")
.data([0].concat(colorScale.quantiles()), function(d) { return d; });
legend.enter().append("g")
.attr("class", "legend");
legend.append("rect")
.attr("x", function(d, i) { return legendElementWidth * i; })
.attr("y", height)
.attr("width", legendElementWidth)
.attr("height", cellSize / 2)
.style("fill", function(d, i) { return color(Math.sqrt(data[i] / Comparison_Type_Max )); });
legend.append("text")
.attr("class", "mono")
.text(function(d) { return "≥ " + Math.round(d); })
.attr("x", function(d, i) { return legendElementWidth * i; })
.attr("y", height + cellSize);
legend.exit().remove();
i don't want to use the colorscale variable instead i want to generate the legend according to the color variable defined in the main code, the colorscale variable is not used anywhere in the code but only in the legend just for a start, the whole calender view graph is generated using the the colorscheme provided by the color variable
I am trying to place text over a donut chart with inner rings created with D3. The plunker code can be accessed here: plunker
For placing the text over the arcs, I want to access the donutData array and place the values over each ring of the chart.
gs.append("text")
.attr("transform", function(d){
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.text(function(d,i){
return donutData[i];
});
Since the donutData array stores the values of "actual" and "predicted" which it uses for creating the donut chart, I want those values to be placed over each of the arcs in the chart.
To add label to the center of the arc you need to add the label to the centroid.
gs.selectAll('text').data(function(d) {
return pie(d);
})
.enter().append("text")
.attr("transform", function(d, i, j) {//calculating the d as done in the path
k = arc.innerRadius(30 + donutWidth * j).outerRadius(donutWidth * (j + 1))(d);;
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) {
return d.data;
});
Working code here
Hope this helps!
You will have to group the arc paths and labels.
var gs = chart1.selectAll("g").data(donutData).enter().append("g");
var groups = gs.selectAll('.arc') //Created groups
.data(function(d) {
return pie(d);
})
.enter().append("g").classed("arc", true);
path = groups.append('path') //Append paths
.attr('d', function(d, i, j) {
return arc.innerRadius(30 + donutWidth * j).outerRadius(donutWidth * (j + 1))(d);
})
.attr('fill', function(d, i) {
return color(i);
})
.on("mouseover", function(d) {
d3.select(this).select("path").transition()
.duration(200)
.attr("d", arcOver);
})
.on("mouseout", function(d) {
d3.select(this).select("path").transition()
.duration(100)
.attr("d", arc);
});
groups.append("text") //Add labels
.attr("transform", function(d, i, j) {
var arc1 = arc.innerRadius(30 + donutWidth * j).outerRadius(donutWidth * (j + 1));
return "translate(" + arc1.centroid(d) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d, i) {
return d.data
});
// Code goes here
(function() {
var margin = {
top: 30,
right: 30,
bottom: 70,
left: 40
},
width = 580 - margin.left - margin.right,
height = 560 - margin.top - margin.bottom,
donutWidth = 100
inner = 70;
radius = Math.min(width, height) / 2;
var color = d3.scale.category10();
var pie = d3.layout.pie()
//.value(function(d){ return d.count;})
.sort(null);
var arc = d3.svg.arc();
//.innerRadius(radius - donutWidth)
//.outerRadius(radius - 50);
var arcOver = d3.svg.arc()
.innerRadius(inner + 5)
.outerRadius(radius + 5);
var chart1 = d3.select("#chartArea1").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
/* d3.csv("data.csv", function(error, data) {
if (error) throw error;*/
var data = [{
"Class": "Class A",
"Actual_Class": 495,
"Predicted_Class": 495,
"Accuracy": 100
}, {
"Class": "Class B",
"Actual_Class": 495,
"Predicted_Class": 492,
"Accuracy": 99
}, {
"Class": "Class C",
"Actual_Class": 495,
"Predicted_Class": 495,
"Accuracy": 100
}, {
"Class": "Class D",
"Actual_Class": 495,
"Predicted_Class": 495,
"Accuracy": 100
}, {
"Class": "Class E",
"Actual_Class": 495,
"Predicted_Class": 495,
"Accuracy": 100
}];
var donutData = new Array,
actualValues = new Array,
predValues = new Array;
data.forEach(function(d) {
actualValues.push(d.Actual_Class);
predValues.push(d.Predicted_Class);
});
console.log(data);
donutData.push(actualValues);
donutData.push(predValues);
console.log(donutData);
var gs = chart1.selectAll("g").data(donutData).enter().append("g");
var groups = gs.selectAll('.arc')
.data(function(d) {
return pie(d);
})
.enter().append("g").classed("arc", true);
path = groups.append('path')
.attr('d', function(d, i, j) {
return arc.innerRadius(30 + donutWidth * j).outerRadius(donutWidth * (j + 1))(d);
})
.attr('fill', function(d, i) {
return color(i);
})
.on("mouseover", function(d) {
d3.select(this).select("path").transition()
.duration(200)
.attr("d", arcOver);
})
.on("mouseout", function(d) {
d3.select(this).select("path").transition()
.duration(100)
.attr("d", arc);
});
groups.append("text")
.attr("transform", function(d, i, j) {
var arc1 = arc.innerRadius(30 + donutWidth * j).outerRadius(donutWidth * (j + 1));
return "translate(" + arc1.centroid(d) + ")";
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d, i) {
return d.data
});
var legend = chart1.selectAll(".legend")
.data(color.domain().slice()) //.reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("x", 190)
.attr("y", -(margin.top) * 7 - 8)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", margin.right * 7)
.attr("y", -(margin.top) * 7)
.attr("dy", ".35em")
.text(function(d, i) {
return data[i].Class;
});
// });
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chartArea1"></div>
I was working on the chord diagram and I am unable to show the total value for each product. I want it to display the total count for each category when I hover over the radial arc for a particular category. The example is taken from http://bl.ocks.org/mbostock/4062006. Any prompt help would be greatly appreciated. Thank you :)
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.chord path {
fill-opacity: .67;
stroke: #000;
stroke-width: .5px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// From http://mkweb.bcgsc.ca/circos/guide/tables/
var matrix = [
[11975, 5871, 8916, 2868],
[ 1951, 10048, 2060, 6171],
[ 8010, 16145, 8090, 8045],
[ 1013, 990, 940, 6907]
];
var chord = d3.layout.chord()
.padding(.05)
.sortSubgroups(d3.descending)
.matrix(matrix);
var width = 960,
height = 500,
innerRadius = Math.min(width, height) * .41,
outerRadius = innerRadius * 1.1;
var fill = d3.scale.ordinal()
.domain(d3.range(4))
.range(["#000000", "#FFDD89", "#957244", "#F26223"]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.append("g").selectAll("path")
.data(chord.groups)
.enter().append("path")
.style("fill", function(d) { return fill(d.index); })
.style("stroke", function(d) { return fill(d.index); })
.attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius))
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
var ticks = svg.append("g").selectAll("g")
.data(chord.groups)
.enter().append("g").selectAll("g")
.data(groupTicks)
.enter().append("g")
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + outerRadius + ",0)";
});
ticks.append("line")
.attr("x1", 1)
.attr("y1", 0)
.attr("x2", 5)
.attr("y2", 0)
.style("stroke", "#000");
ticks.append("text")
.attr("x", 8)
.attr("dy", ".35em")
.attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180)translate(-16)" : null; })
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.text(function(d) { return d.label; });
svg.append("g")
.attr("class", "chord")
.selectAll("path")
.data(chord.chords)
.enter().append("path")
.attr("d", d3.svg.chord().radius(innerRadius))
.style("fill", function(d) { return fill(d.target.index); })
.style("opacity", 1)
.append("svg:title").text(function(d, i) { return "Value : " + d.source.value });;
// Returns an array of tick angles and labels, given a group.
function groupTicks(d) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(0, d.value, 1000).map(function(v, i) {
return {
angle: v * k + d.startAngle,
label: i % 5 ? null : v / 1000 + "k"
};
});
}
// Returns an event handler for fading a given chord group.
function fade(opacity) {
return function(g, i) {
svg.selectAll(".chord path")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition()
.style("opacity", opacity);
};
}
</script>
`