问题描述
此 D3 示例作为我的起点:
可以注意到至少两个问题:
- 图例是错误的。这是因为它仍然包含原始示例中的硬编码名称。
- 所有节点都是黑色的。这是因为对于原始示例中的节点名称,颜色方案也是硬编码。
如何改进原始示例
您可以使用序数刻度将颜色映射到不同的节点名称。实施它只需要对现有代码进行一些细微的更改。
步骤1.为颜色创建顺序标度
不是颜色只是一个颜色名称列表,硬编码为特定名称,请使用,并设置 .range()是要使用的颜色的数组。例如:
var colors = d3.scale.ordinal()
.range([#5687d1, #7b615c,#de783b,#6ab975,#a173d1,#bbbbbb]);
这将创建一个使用与原始可视化相同颜色的序数刻度。由于您的数据需要更多的颜色,因此您需要向您的范围添加更多颜色,否则将重复颜色。
作为一个快捷方式,您可以使用让d3为你选择一个范围20个分类颜色。
现在为路径设置填充颜色元素弧和面包屑,您只需使用 colors(d.name)而不是 colors [d.name] 。
步骤2.使用您的数据构建量表的域
c $ c> .domain()将在我们有数据之后设置,因为它将取决于数据中包含的唯一名称的列表。为此,我们可以循环访问数据,并创建一个唯一名称的数组。可能有几种方法可以做到这一点,但这里有一个很好的工作:
var uniqueNames =(function(a){
var output = [];
a.forEach(function(d){
if(output.indexOf(d.name)=== -1){
output.push (d.name);
}
});
return output;
})(nodes);
这会创建一个空数组,然后循环遍历 nodes 数组,如果节点的名称在新数组中不存在,则添加它。
是颜色范围的域:
colors.domain(uniqueNames);
步骤3.使用秤的域构建图例
由于图例将取决于域,请确保在设置域后调用 drawLegend()函数。
您可以通过调用 colors.domain()来找到域中的元素数量(用于设置图例的高度)。length 。然后对于图例的 .data(),您可以使用域本身。最后,为了设置图例框的填充颜色,您可以在 d 上调用颜色标度,因为域中的每个元素都是名称。下面是这些例子在实践中的三个变化:
var legend = d3.select(#legend)。 append(svg:svg)
.attr(width,li.w)
.attr(height,colors.domain()。length *(li.h + li.s ));
var g = legend.selectAll(g)
.data(colors.domain())
.enter()。append(svg:g)
.attr(transform,function(d,i){
returntranslate(0,+ i *(li.h + li.s)+);
} );
g.append(svg:rect)
.attr(rx,li.r)
.attr(ry,li.r)
.attr(width,li.w)
.attr(height,li.h)
.style(fill,function(d){return colors(d); });
就是这样。希望有帮助。
以下是更新的。
This D3 example served as my starting point:
http://bl.ocks.org/kerryrodden/7090426
I wanted to change data that feeds the diagram, and I made following new example:
One can notice at least two problems:
- Legend is wrong. This is because it still contains 'hardcoded' names from original example.
- All nodes are colored black. This is because the color scheme is also 'hardcoded' only for node names from original example.
How to improve the original example (or my jsfiddle, it doesn't matter) so that legend and coloring are self-adjusted to the data that feeds the diagram?
You can use an ordinal scale to map colors to the different node names. Implementing it would only require a few minor changes to your existing code.
Step 1. Create an ordinal scale for the colors
Instead of having colors be simply a list of color names, hard-coded to specific names, use d3.scale.ordinal(), and set the .range() to be an array of the colors you want to use. For example:
var colors = d3.scale.ordinal() .range(["#5687d1","#7b615c","#de783b","#6ab975","#a173d1","#bbbbbb"]);
This would create an ordinal scale that uses the same colors as the original visualization. Since your data would require more colors, you would want to add a few more to your range, otherwise colors will be repeated.
As a shortcut, you can use d3.scale.category20() to let d3 choose a range 20 categorical colors for you.
Now when setting the fill colors for your path element arcs and also your breadcrumbs, you would simply use colors(d.name) instead of colors[d.name].
Step 2. Use your data to construct the domain of the scale
The .domain() of this scale will be set once we have the data, since it will depend on a list of the unique names contained in the data. To do this, we can loop through the data, and create an array of the unique names. There are probably several ways to do this, but here's one that works well:
var uniqueNames = (function(a) { var output = []; a.forEach(function(d) { if (output.indexOf(d.name) === -1) { output.push(d.name); } }); return output; })(nodes);
This creates an empty array, then loops through each element of the nodes array and if the node's name doesn't already exist in the new array, it is added.
Then you can simply set the new array to be the domain of the color scale:
colors.domain(uniqueNames);
Step 3. Use the scale's domain to build the legend
Since the legend is going to depend on the domain, make sure the drawLegend() function is called after the domain is set.
You can find the number of elements in the domain (for setting the height of the legend) by calling colors.domain().length. Then for the legend's .data(), you can use the domain itself. Finally, to set the fill color for the legend boxes, you call the color scale on d since each element in the domain is a name. Here's what those three changes to the legend look like in practice:
var legend = d3.select("#legend").append("svg:svg") .attr("width", li.w) .attr("height", colors.domain().length * (li.h + li.s)); var g = legend.selectAll("g") .data(colors.domain()) .enter().append("svg:g") .attr("transform", function(d, i) { return "translate(0," + i * (li.h + li.s) + ")"; }); g.append("svg:rect") .attr("rx", li.r) .attr("ry", li.r) .attr("width", li.w) .attr("height", li.h) .style("fill", function(d) { return colors(d); });
And that's about it. Hope that helps.
Here's the updated JSFiddle.
这篇关于改进D3序列Sunburst示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!