I am trying to create a piechart for some data I have in a JSON file, however I keep getting an error and I am not sure why.
My JSON file is like this:
{"data":[
{"ap": [
{"floorratio": [
{"floor":"Basement", "ratio": 0.20},
{"floor":"Ground", "ratio": 0.20},
{"floor":"First Floor", "ratio": 0.15},
{"floor":"Second Floor", "ratio": 0.20},
{"floor":"Third Floor", "ratio": 0.25}
]}
]},
{"ap": [
{"floorratio": [
{"floor":"Basement", "ratio": 0.10},
{"floor":"Ground", "ratio": 0.30},
{"floor":"First Floor", "ratio": 0.10},
{"floor":"Second Floor", "ratio": 0.15},
{"floor":"Third Floor", "ratio": 0.35}
]}
]}
]}
and my HTML code is:
<!doctype html>
<html>
<head>
<title>Pie Chart Test</title>
<script src="d3.min.js"></script>
</head>
<style>
body {
font: 10px sans-serif;
}
.arc path {
stroke: #fff;
}
</style>
<body>
<script>
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.layout.pie()
.sort(null)
.value(function (d) {
return d.data[0].ap[0].floorratio[0].ratio;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.json("APCategories.json", function (data) {
var g = svg.selectAll(".arc")
.data(pie(function (d) { return d.data[0].ap[0].floorratio[0]}))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function (d) {
return color[d.data[0].ap[0].floorratio[0].floor];
});
g.append("text")
.attr("transform", function (d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function (d) {
return d.data[0].ap[0].floorratio[0].floor;
});
});
</script>
</body>
</html>
I keep getting the error when I inspect the element that i.map is not a function. Am I reading the data into the code incorrectly? Any help would be appreciated.
edit: I am currently only trying to load the first ap, the json file will be much bigger and I will be creating a pie chart that changes so that it represents each ap.floorratio.
So for now, only the data from the first floorratio array needs to go into the pie chart.
Here's a working version.
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.svg.arc()
.outerRadius(radius - 10)
.innerRadius(0);
// defines wedge size
var pie = d3.layout.pie()
.sort(null)
.value(function (d) { return d.ratio; });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.json("APCategories.json", function(error, data) {
node = data.data[0].ap[0].floorratio; // <------ here
var g = svg.selectAll(".arc")
.data(pie(node))
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.floor); });
g.append("text")
.attr("transform", function (d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function (d) { return d.data.floor; });
});
The [0] after floorratio wasn't necessary.
http://bl.ocks.org/steveharoz/0638d230c133da1de385
Related
I am seeking assistance with my script in creating a column chart with similar string occurrences listed in a CSV file. I am creating this chart using D3.js V3. The script and technique I’m using to capture the length of the items return results, but the number count of the length is not close to the actual count, very short from the expect item count. Can someone please review my script and see what is causing my length count to be way off from the actual number count. Any help, suggestion, or techniques will be greatly appreciated.
Below is a sample of the CSV file concept and D3 script. Thank you in advance.
d3.csv("../../data/teamData.csv", function(error, csv_data) {
if (error){
alert("Data didn't load, Refresh your browser");
}else{
for (var i = 0; i < csv_data.length; i++) {
if(csv_data[i].Team == "Team 1"){
team1 = csv_data[i].Team;
}
if(csv_data[i].Team == "Team 2"){
team2 = csv_data[i].Team;
}
}
}
var teamCount1 = team1.length;
var teamCount2 = team2.length;
console.log(teamCount1);//Not showing correct number count
console.log(teamCount2);//Not showing correct number count
var margin = {top: 2, right: 2, bottom: 60, left: 2},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var barPadding = 1;
var chartdata = [teamCount1, teamCount2];
var textData = ['Team 1', 'Team 2'];
var xScale = d3.scale.ordinal()
.domain(d3.range(chartdata2.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(chartdata)])
.range([0, h]);
var svg = d3.select(".chartData")
.append("svg")
.attr("preserveAspectRatio", "xMidYMin")
.attr("viewBox", "0 0 1000 650")
.attr("width", "100%")
.attr("height", "100%")
.attr("display",'block')
.append("g").attr("class","stackChart2")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Create bars
svg.selectAll("barRect")
.data(chartdata)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
})
.on("click", function() {
sortBars();
});
//Create labels
svg.selectAll("text")
.data(chartdata)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d) - 10;
})
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("fill", "black");
//Text Label
var yTextPadding = 20;
svg.selectAll(".bartext2")
.data(textData)
.enter()
.append("text")
.attr("class", "bartext2")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("font-family", "sans-serif")
.style("font-size", "16px")
.attr("x", function(d,i) {return xScale(i)+xScale.rangeBand()/2;})
.attr("y", 460)
.text(function(d){return d;});
//Define sort order flag
var sortOrder = false;
//Define sort function
var sortBars = function() {
//Flip value of sortOrder
sortOrder = !sortOrder;
svg.selectAll("rect")
.sort(function(a, b) {
if (sortOrder) {
return d3.ascending(a, b);
} else {
return d3.descending(a, b);
}
})
.transition()
.delay(function(d, i) {
return i * 50;
})
.duration(1000)
.attr("x", function(d, i) {
return xScale(i);
});
}
});//End of D3 function
I was able to figure it out referencing this stackoverflow post: D3 Loading in CSV file then using only specific columns. I was able to filter out the needed column from my CSV file which was "Team", using d3 map as follow; var col2data = csv_data.map(function(d) { return d.Team });
Then I followed this stackoverflow post to filter my specific reoccurring item from the "Team" column: d3.js filter from csv file using multiple columns. This post made it possible to get the length of the items to build my column chart. All is working as expected. Below is the edited script. I hope this will help someone in the future encountering this issue.
d3.csv("../../data/teamData.csv", function(csv_data) {
//Filter CSV File to Specific Column
var col2data = csv_data.map(function(d) { return d.Team });
console.log(col2data);
//Filter CSV Column for Specific Column Item
var team1 = csv_data.filter(function(d){
if( d["Team"] == "Team 1"){
return d;
}
})
var team2 = csv_data.filter(function(d){
if( d["Team"] == "Team 2"){
return d;
}
})
console.log(team1);
var teamCount1 = team1.length;
var teamCount2 = team2.length;
//console.log(teamCount1);//Not showing correct number count
//console.log(teamCount2);//Not showing correct number count
var margin = {top: 2, right: 2, bottom: 60, left: 2},
w = 960 - margin.left - margin.right,
h = 500 - margin.top - margin.bottom;
var barPadding = 1;
var chartdata = [teamCount1, teamCount2];
var textData = ['Team 1', 'Team 2'];
var xScale = d3.scale.ordinal()
.domain(d3.range(chartdata.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(chartdata)])
.range([0, h]);
var svg = d3.select(".plot-div")
.append("svg")
.attr("preserveAspectRatio", "xMidYMin")
.attr("viewBox", "0 0 1000 650")
.attr("width", "100%")
.attr("height", "100%")
.attr("display",'block')
.append("g").attr("class","stackChart2")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//Create bars
svg.selectAll("barRect")
.data(chartdata)
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
})
.on("click", function() {
sortBars();
});
//Create labels
svg.selectAll("text")
.data(chartdata)
.enter()
.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d) - 10;
})
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("fill", "black");
//Text Label
var yTextPadding = 20;
svg.selectAll(".bartext2")
.data(textData)
.enter()
.append("text")
.attr("class", "bartext2")
.attr("text-anchor", "middle")
.attr("fill", "black")
.attr("font-family", "sans-serif")
.style("font-size", "16px")
.attr("x", function(d,i) {return xScale(i)+xScale.rangeBand()/2;})
.attr("y", 460)
.text(function(d){return d;});
//Define sort order flag
var sortOrder = false;
//Define sort function
var sortBars = function() {
//Flip value of sortOrder
sortOrder = !sortOrder;
svg.selectAll("rect")
.sort(function(a, b) {
if (sortOrder) {
return d3.ascending(a, b);
} else {
return d3.descending(a, b);
}
})
.transition()
.delay(function(d, i) {
return i * 50;
})
.duration(1000)
.attr("x", function(d, i) {
return xScale(i);
});
}
});//End of D3 function
Instead of loading data from a CSV file, I would like to load JSON data from a variable.
let jsonData = [
{"Date": "1/1/2014 01:00", "Air_Temp": 3.1},
{"Date": "1/1/2014 02:00", "Air_Temp": 3.2},
{"Date": "1/1/2014 03:00", "Air_Temp": 1.6},
{"Date": "1/1/2014 04:00", "Air_Temp": 1.0},
{"Date": "1/1/2014 05:00", "Air_Temp": 2.3}
];
The graph is being displayed with no errors logged to the console, but the data is not being loaded.
https://jsfiddle.net/s9cbwvL1/1/
Fyi
you are not using your own created function
parseDate function and type function
when i read that function, i think somebody manage to rearange the d.Date on the data
result
your axis domain is [ invalid date, invalid date ]
solution
i convert d.Date to proper format
you sending data on each item not in array
result
your chart not drawing nor having error
solution
i make it double array on jsonData
Line_chart.data([jsonData])
context.data([jsonData])
take a look
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script type="text/javascript">
//Example: https://bl.ocks.org/EfratVil/92f894ac0ba265192411e73f633a3e2f
let jsonData1 =
[
{"Date": "1/1/2014 01:00", "Air_Temp": 3.1},
{"Date": "1/1/2014 02:00", "Air_Temp": 3.2},
{"Date": "1/1/2014 03:00", "Air_Temp": 1.6},
{"Date": "1/1/2014 04:00", "Air_Temp": 1.0},
{"Date": "1/1/2014 05:00", "Air_Temp": 2.3}
];
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 110, left: 40},
margin2 = {top: 430, right: 20, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
height2 = +svg.attr("height") - margin2.top - margin2.bottom;
var parseDate = d3.timeParse("%m/%d/%Y %H:%M");
let jsonData = jsonData1.map(function(d,i){ d.Date = parseDate(d.Date); return d })
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
var line = d3.line()
.x(function (d) { return x(d.Date); })
.y(function (d) { return y(d.Air_Temp); });
var line2 = d3.line()
.x(function (d) { return x2(d.Date); })
.y(function (d) { return y2(d.Air_Temp); });
var clip = svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("x", 0)
.attr("y", 0);
var Line_chart = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("clip-path", "url(#clip)");
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
x.domain(d3.extent(jsonData, function(d) { return d.Date; }));
y.domain([0, d3.max(jsonData, function (d) { return d.Air_Temp; })]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
Line_chart.selectAll('path')
.data([jsonData])
.enter()
.append("path")
.attr("class", "line")
.attr("d", function (d){ line });
context.append("path")
.data([jsonData])
.attr("class", "line")
.attr("d", line2);
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
Line_chart.select(".line").attr("d", line);
focus.select(".axis--x").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
Line_chart.select(".line").attr("d", line);
focus.select(".axis--x").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
function type(d) {
d.Date = parseDate(d.Date);
d.Air_Temp = +d.Air_Temp;
return d;
}
</script>
</body>
</html>
You need to convert the dates
var parseDate = d3.timeParse("%m/%d/%Y %H:%M");
jsonData.forEach( d => { d.Date = parseDate(d.Date); });
And pass the dataset as one to the path elements
Line_chart.append("path")
.data([jsonData])
.attr("class", "line")
.attr("d", line);
context.append("path")
.data([jsonData])
.attr("class", "line")
.attr("d", line2);
Is there a way to minimize the padding between ranges in a grouped horizontal bar chart?
I am building a horizontal bar chart directive for AngularJS and at the moment I am pretty close but it's the spacing that I am not exactly happy with. If there is a better way to build the vertical ranges I would very much appreciate any tips. Here's my result so far:
angular.module('MissionControlApp').directive('d3GroupedHorizontalBarChart2', ['d3', '$timeout', function(d3, $timeout) {
return {
restrict: 'E',
scope: {
data: '=',
onClick: '&d3OnClick'
},
link: function(scope, ele) {
var refreshScope = function() {
scope.$apply();
};
var svg = d3.select(ele[0])
.append("svg")
.attr("width", "100%");
// on window resize, re-render d3 canvas
window.onresize = function() {
return scope.$apply();
};
scope.$watch(function(){
return angular.element(window)[0].innerWidth;
}, function(){
return scope.render(scope.data);
}
);
// watch for data changes and re-render
scope.$watch("data", function(newVals) {
if(!newVals) return;
return scope.render(newVals);
}, true);
// define render function for grouped bar charts
scope.render = function(data){
if(!data) return;
// remove all previous items before render
svg.selectAll("*").remove();
// setup variables
var margin = {top: 25, right: 40, bottom: 70, left: 150};
var width = d3.select(ele[0])._groups[0][0].offsetWidth - margin.left - margin.right;
var height = (scope.data.length * 60);
svg.attr('height', height + margin.top + margin.bottom);
var y0 = d3.scaleBand()
.rangeRound([0, height])
.paddingInner(0.01);
var y1 = d3.scaleBand()
.padding(0.01);
var x = d3.scaleLinear()
.rangeRound([0, width]);
var color = d3.scaleLinear()
.domain([0, 25, 50, 75, 100])
.range(["#51b75d", "#90eb9d","#ffff8c","#f5c93f","#c45c44"])
.interpolate(d3.interpolateHcl);
var xAxis = d3.axisBottom(x)
.tickSizeInner(-(height-5))
.tickPadding(8);
var keys = d3.keys(data[0]).filter(function(key) { return key !== "user"; });
y0.domain(data.map(function(d) { return d.user; }));
y1.domain(keys).rangeRound([0, y0.bandwidth()]);
x.domain([0, 100]);
// Define bars
var bar = svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("transform", function(d) { return "translate(" + margin.left + "," + (y0(d.user) + (y0.bandwidth()/2) + margin.top - y1.bandwidth()) + ")"; });
var barEnter = bar.selectAll("rect")
.data(function(d) { return d.values; })
.enter();
barEnter.append("rect")
.attr("height", y1.bandwidth())
.attr("y", function(d) {return y1(d.name); })
.attr("x", 0)
.attr("value", function(d){return d.name;})
.attr("width", 0)
.attr("fill", function(d) { return color(d.value); })
.on("mouseover", function() { d3.select(this).style("cursor", "pointer");})
.on("mouseout", function() { d3.select(this).style("cursor", "default");})
.on("click", function(d){
scope.onClick({item: d});
d3.select(".selectedBlueFill").classed("selectedBlueFill", false);
d3.select(this).classed("selectedBlueFill", true);
$timeout(refreshScope, 0, false); // flush the digest cycle
})
.transition()
.duration(1000)
.attr("width", function(d) { return x(d.value); });
barEnter.append("text")
.attr("fill", "#000")
.attr("y", function(d){return y1(d.name) + (y1.bandwidth() / 2);})
.attr("x", function(d){return x(d.value);})
.attr("dx", 5)
.attr("dy", ".35em")
.text(function(d){return parseFloat(d.value).toFixed(0) + "%";})
.attr("fill-opacity", 0)
.transition()
.duration(1500)
.attr("fill-opacity", 1);
// Set up x axis
svg.append("g")
.attr("class", "axisHorizontal")
.attr("transform", "translate(" + margin.left + "," + (height + margin.top) + ")")
.call(xAxis);
// Set up y axis
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(d3.axisLeft(y0));
// Draw the legend
// Create the gradient for the legend
svg.append("defs")
.append("linearGradient")
.attr("id", "legend-traffic")
.attr("x1", "0%").attr("y1", "0%")
.attr("x2", "100%").attr("y2", "0%")
.selectAll("stop")
.data(color.range())
.enter().append("stop")
.attr("offset", function(d,i) { return i/(color.range().length-1); })
.attr("stop-color", function(d) { return d; });
// Legend variables
var legendWidth = width * 0.6;
var legendHeight = 10;
// Legend container
var legendSvg = svg.append('g')
.attr("class", "legendWrapper")
.attr("transform", "translate(" + ((width + margin.left + margin.right)/2) + "," + (height + margin.top + margin.bottom) + ")");
// Draw the rectangle
legendSvg.append("rect")
.attr("class", "legendRect")
.attr("x", -legendWidth/2)
.attr("y", -30)
.attr("width", legendWidth)
.attr("height", legendHeight)
.attr("fill", "url(#legend-traffic)");
// Append title
legendSvg.append("text")
.attr("class", "legendTitle")
.attr("x", 0)
.attr("y", -35)
.attr("text-anchor", "middle")
.text("Worksets Opened %");
// Set scale for x-axis
var xScale = d3.scaleLinear()
.range([0, legendWidth])
.domain([0,100]);
// Define x-axis
var legendAxis = d3.axisBottom(xScale).ticks(5);
// Set up x-axis
legendSvg.append("g")
.attr("class", "axisLegend")
.attr("transform", "translate(" + (-legendWidth/2) + "," + (legendHeight-30) + ")")
.call(legendAxis);
};
}
};
}]);
However the result I am getting is with large inner spacing. I am setting the .paddingInner property to 0.001 and still pretty much end up with large spacing......ideas?
The idea would be to make groups, where each group hold unique bars for the group.
Now once you have the group you can alter the between distance with some maths, as shown below in snippet below:
var bar = chart
.selectAll("g")
.data(zippedData)
.enter().append("g")
.attr("transform", function(d, i) {
//here barHeight is the width of the bars.
return "translate(" + spaceForLabels + ","
+ (i * barHeight + gapBetweenGroups * (0.5 + Math.floor(i/data.series.length))) + ")";
});
By changing value of gapBetweenGroups you can regulate the distance of the groups.
Working code here
I want to change the color of nodes when clicking on it. It should be set in the "click" function.
However, in the click function, "d" is the node element, but to change node color I have to select the "circle" element, which is append to node using nodeEnter.append("circle"). How can I achieve it?
the html file:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text {
font: 30px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 7px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 20, bottom: 20, left: 20},
width = 1200 - margin.right - margin.left,
height = 800 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree();
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.x, d.y]; });
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("parse_tree.json", function(error, parse_tree) {
if (error) throw error;
root = parse_tree;
root.x0 = height / 2;
root.y0 = 0;
tree.size([width, height]);
update(root);
});
function update(source1) {
var originalConsole = console;
var nodes = tree.nodes(root);
var links = tree.links(nodes);
nodes.forEach(function(d) { d.y = d.depth * 100; });
// 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(" + source1.y0 + "," + source1.x0 + ")"; })
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("text")
.attr("x", function(d) { return d.children || d._children ? -20 : 20; })
.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.x + "," + d.y + ")"; });
nodeUpdate.select("circle")
.attr("r", 10)
.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(" + source1.y + "," + source1.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: source1.x0, y: source1.y0};
return diagonal({source: o, target: o});
})
.style("stroke-width", "3px")
.style("stroke", "green");
// 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: source1.x, y: source1.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;
});
for(var k = 0; k < 1000; k++)
flag = 0;
}
// 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>
the json file:
{"name":"VP",
"size":"89",
"children": [
{"name":"VBP",
"size":"15",
"children":[{"name":"are", "size":"38"}]
},
{"name":"NP",
"size":"83",
"children": [
{"name":"DT",
"size":"29",
"children":[{"name":"a", "size":"53"}]
},
{"name":"NN",
"size":"50",
"children":[{"name":"boy", "size":"99"}]
}
]
}
]
}
d is the data, not the node
this in the click function is the dom node
d3.select(this) is the selection that picks just that node as a d3 selection
so to get a child circle element in a selection, you would do
d3.select(this).select("circle")
Now you'd think you'd just add a style("fill", "red") or whatever to this and job done, but as this is the oft-forked example from Mike Bostock that style would get overridden again in the transition update. So what you need to do is set a property on the data and then get that picked up by the drawing in the enter/update/exit sequence.
In the end all you need in the click function is:
d.clicked = true;
and in the nodeUpdate section do this -->
nodeUpdate.select("circle")
.attr("r", function(d){ return computeRadius(d); })
.style("fill", function(d) { return d.clicked ? "red" : (d._children ? "lightsteelblue" : "#fff");});
Note this doesn't do anything about un-highlighting a node. (You'd have to toggle the previous d.clicked boolean value)
I have the following code , and i'm trying to append text to my links (i need to represent country names on top of the link), notice that the text is embedded within a json list, and that's why i think i cant get it into my links:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Collapsible Tree Example</title>
<style>
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text { font: 12px sans-serif; }
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
</style>
</head>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var treeData = [
{"name":["1"],"weights":["17284"],"variableNames":["Country name"],"criteria":
{"levels":["","Brazil","Canada","France","Germany","Spain","Turkey","United Kingdom","United States"]},
"children":[{"name":["2"],"weights":["5920"],"variableNames":["Country name"],
"criteria":{"levels":["","Brazil","Canada","France","Germany","Spain","Turkey","United Kingdom","United States"]},
"children":[{"name":["3 "],"weights":["604"],"prediction":["0.256"]},{"name":["4 "],"weights":["5316"],"prediction":["0.231"]}],"prediction":["0.233"]},{"name":["5"],"weights":["11364"],
"variableNames":["Country name"],"criteria":{"levels":["","Brazil","Canada","France","Germany","Spain","Turkey","United Kingdom","United States"]},
"children":[{"name":["6 "],"weights":["10029"],"prediction":["0.153"]},{"name":["7 "],"weights":["1335"],
"prediction":["0.053"]}],"prediction":["0.141"]}],"prediction":["0.173"]}
];
// ************** Generate the tree diagram *****************
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 960 - margin.right - margin.left,
height = 500 - margin.top - margin.bottom;
var i = 0;
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 + ")");
root = treeData[0];
update(root);
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
// Declare the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter the nodes.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")"; });
nodeEnter.append("circle")
.attr("r", 10)
.style("fill", "#fff");
nodeEnter.append("text")
.attr("x", 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", 1);
// Declare the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter the links.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", diagonal);
link.append("text")
.attr("x", 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.source.criteria.levels; }) // also tried d.source.criteria
.style("fill-opacity", 1);
}
</script>
</body>
</html>
I tried to get help from other questions like this one: Adding text labels to force directed graph links in d3.js
But couldn't find any question that had an embedded list.
Thanks in Advance!!
The key is that you define the links setup just like the nodes, i.e. build a g that contains the path and the text. In your example, the text ends up in the path elements, respectively.
So this is key:
var linkG = link.enter()
.append('g')
.attr('class', 'link');
Here's a fiddle
(I changed a few bit here and there as well... not that I wasn't sure what texts you want to display)