我试图通过下拉菜单更新我的甜甜圈图,但是,执行函数时收到两个错误。我要创建的是一张表示特定医院以及多个类别(例如疾病,受伤等)的甜甜圈图。我使用d3.nest()将我的数据集放入嵌套的数据数组中。
方法:
d3.json("hospitals.json", function (error, dataset) {
if(error) throw error;
var categoryByHospitals = d3.nest()
.key(function(d) {return d.hospitalName;})
.entries(dataset);
categoryByHospitals.forEach(function(object) {
object.values.forEach(function(d) {
d.count = +d.count;
d.enabled = true;
});
我正在使用d3 v4,并在该网站上查看我的甜甜圈图:https://bl.ocks.org/tezzutezzu/c2653d42ffb4ecc01ffe2d6c97b2ee5e
下面包含屏幕截图和代码
JavaScript代码
var timeDuration = 600;
var firstTime = true;
var width = 800;
var height = 800;
var radius = Math.min(width, height) / 2;
var donutWidth = 100;
var legendRectSize = 25;
var legendSpacing = 6;
var color = d3.scaleOrdinal(d3.schemeCategory20b);
var svg = d3.select('#chart')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')');
var arc = d3.arc()
.innerRadius(radius - donutWidth)
.outerRadius(radius);
var pie = d3.pie()
.value(function(d) { return d.count; })
.sort(null);
var total = d3.sum(dataset, d => d.count);
var newTotal = d3.select('.new-total-holder')
.append('span')
.attr('class', 'newTotal').text(total);
var tooltip = d3.select('#chart')
.append('div')
.attr('class', 'tooltip');
tooltip.append('div')
.attr('class', 'label');
tooltip.append('div')
.attr('class', 'count');
tooltip.append('div')
.attr('class', 'percent');
var path = svg.selectAll('path')
.data(pie(categoryByHospitals[17].values)) //.data(pie(dataset))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d) { return color(d.data.category); })
.each(function(d) { this._current - d; });
path.on('mouseover', function(d) {
//doesn't work
var total = d3.sum(d3.values(categoryByHospitals).map(function(d) {
return (d.enabled) ? d.count : 0;
}));
var percent = Math.round(1000 * d.data.count / total) / 10;
tooltip.select('.label').html(d.data.label);
tooltip.select('.count').html('Count: ' + d.data.count);
tooltip.select('.percent').html(percent + '%')
tooltip.style('display', 'block');
});
path.on('mouseout', function() {
tooltip.style('display', 'none');
});
path.on('mousemove', function(d) {
tooltip.style('top', (d3.event.layerY + 10) + 'px')
.style('left', (d3.event.layerX + 10) + 'px');
});
var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = -2 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color)
.on('click', function(label) {
var rect = d3.select(this);
var enabled = true;
var totalEnabled = d3.sum(d3.values(categoryByHospitals).map(function(d) {
return (d.value.enabled) ? 1 : 0;
}));
if (rect.attr('class') === 'disabled') {
rect.attr('class', '');
} else { // else
if (totalEnabled < 2) return;
rect.attr('class', 'disabled');
enabled = false;
}
pie.value(function(d) {
if (d.category === label) d.value.enabled = enabled;
return (d.value.enabled) ? d.value.count : 0;
});
path = path.data(pie(categoryByHospitals[17].values));
path.transition()
.duration(750)
.attrTween('d', function(d) {
var interpolate = d3.interpolate(this._current, d); // this = current path element
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
});
var newTotalCalc = d3.sum(categoryByHospitals.filter(function(d) { return d.enabled; }), d => d.count)
// console.log(newTotalCalc);
newTotal.text(newTotalCalc);
});
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return d; }); // return label
//Filter by hospital
var menu = d3.select(".dropDownMenu").selectAll("option")
.data(categoryByHospitals)
.enter()
.append("option");
menu.attr("value", function(d) { return d.key; })
.on("change", change)
.each(change)
.property("checked", true);
function change(category) {
var path = svg.selectAll("path");
var data0 = path.data(),
data1 = pie(category.values);
path = path.data(data1, key);
path
.transition()
.duration(timeDuration)
.attrTween("d", arcTween)
path
.enter()
.append("path")
.each(function(d, i) {
var narc = findNeighborArc(i, data0, data1, key);
if (narc) {
this._current = narc;
this._previous = narc;
} else {
this._current = d;
}
})
.attr("fill", function(d, i) {
return color(d.data.category)
})
.transition()
.duration(timeDuration)
.attrTween("d", arcTween)
path
.exit()
.transition()
.duration(timeDuration)
.attrTween("d", function(d, index) {
var currentIndex = this._previous.data.category;
var i = d3.interpolateObject(d, this._previous);
return function(t) {
return arc(i(t))
}
})
.remove()
firstTime = false;
}
function key(d) {
return d.data.category;
}
function type(d) {
d.count = +d.count;
return d;
}
function findNeighborArc(i, data0, data1, key) {
var d;
if (d = findPreceding(i, data0, data1, key)) {
var obj = cloneObj(d)
obj.startAngle = d.endAngle;
return obj;
} else if (d = findFollowing(i, data0, data1, key)) {
var obj = cloneObj(d)
obj.startAngle = d.endAngle;
return obj;
}
return null
}
function findPreceding(i, data0, data1, key) {
var m = data0.length;
while (--i >= 0) {
var k = key(data1[i]);
for (var j = 0; j < m; j++) {
if (key(data0[j]) === k) return data0[j];
}
}
}
function findFollowing(i, data0, data1, key) {
var n = data1.length,
m = data0.length;
while (++i < n) {
var k = key(data1[i]);
for (var j = 0; j < m; j++) {
if (key(data0[j]) === k) return data0[j];
}
}
}
function arcTween(d) {
var i = d3.interpolate(this._current, d);
this._current = i(0);
return function(t) {
return arc(i(t))
}
}
function cloneObj(obj) {
var o = {};
for (var i in obj) {
o[i] = obj[i];
}
return o;
}
JSON文件
[{"hospitalName": "hospital1", "label":"100", "category": "Sickness", "females":"11", "males":"0", "unknown":"0", "count": "11"},
{"hospitalName": "hospital1", "label": "321.0", "category": "Injury", "females":"0", "males":"1", "unknown":"2", "count": "3"},
{"hospitalName": "hospital1" "label": "211","category": "Mental Disorders", "females":"2", "males":"0", "unknown":"3", "count": "5"},
{"hospitalName": "hospital1","label": "254", "category": "Diseases","females":"1", "males":"0", "unknown":"0", "count": "1"},
{"hospitalName": "hospital2","label": "000", "category": "None", "females":"2", "males":"0", "unknown":"0", "count": "2"},
{"hospitalName": "hospital2", "label": "892.3", "category": "Symptoms","females":"9", "males":"2", "unknown":"0", "count": "11"},
{"hospitalName": "hospital3","label": "000", "category": "None", "females":"1", "males":"0", "unknown":"0", "count": "1"},
{"hospitalName": "hospital3", "label": "100", "category": "Sickness","females":"2", "males":"2", "unknown":"0", "count": "4"}]
HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 Visualization-Donut Chart (V2)</title>
<link href="style(v2).css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Ubuntu" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Oswald" rel="stylesheet">
</head>
<body>
<div class="dropDownMenu">
<select id="selection">
<option id="mtf1" value="hospital1">hospital 1</option>
<option id="mtf2" value="hospital2">hospital 2</option>
<option id="mtf3" value="hospital3">hospital 3</option>
</select>
</div>
<div id="chart"></div>
</body>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.js'>. </script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js'></script>
<script src="scriptsv2.js"></script>
</html>