以下玩具问题说明了我的问题。一张藏宝图说,我有一系列的“位置”。数组中的每个项目(例如怪物或宝藏)都可以存在于地图上的多个位置。例如
locations = [
{name:'treasure', color: 'blue', coords:[[100,100], [200,300]]},
{name:'monsters', color: 'red', coords:[[100,150], [220,420], [50,50]]}
]
现在,我想使用D3绘制这些图形。不良/幼稚的方法(有效-see here for fiddle)如下所示:
for location in locations
for coords in location.coords
svg.append('circle')
.attr('cx', coords[0])
.attr('cy', coords[1])
.attr('r', 8)
.style('fill', location.color)
.datum(location)
但是,当我修改数据的内容时,我不想每次都运行此幼稚的代码。看来使用data()和enter()是做到这一点的“正确”方法,但我无法弄清楚它如何与子坐标一起工作。例如
svg.selectAll('circle').data(locations).enter().append('circle')
.attr('cx', (d) -> d.coords[0][0])
.attr('cy', (d) -> d.coords[0][1])
.attr('r', 8)
.style('fill', (d) -> d.color)
这很好用,但是如您所见,我只在每个位置都打印所有位置的第一坐标。我怀疑这样做的唯一方法是展平我的数据数组,因此总共有5个条目-3个怪物和2个宝藏物品。
只是想知道是否有一种方法可以使用D3更好地处理此问题。
最佳答案
为此,您需要nested selections。这个想法是,您可以附加多个元素,而不是每个数据项附加单个元素。在代码中,它看起来像这样:
// append a `g` element for each data item to hold the circles
var groups = svg.selectAll("g.circle").data(locations)
.enter().append("g").attr("class", "circle");
// now select all the circles in each group and append the new ones
groups.selectAll("circle")
// the d in this function references a single data item in locations
.data(function(d) { return d.coords; })
.enter().append("circle")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; });
它对更新和退出选择的作用相同。