D3中如何结合堆叠面积图和刷选。

4
我正在努力将D3中的堆叠面积图与brushing功能相结合。我已经成功生成了大型(焦点)和小型(上下文)轴,开始时绘图看起来很好。我的问题是刷选功能根本不起作用。我已经花费了一些时间努力解决这个问题,但无济于事,无法确定问题出在哪里。任何帮助都将不胜感激!
单击此处查看绘图的当前状态。以下是代码:
<!-- language: lang-js -->
<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
    font: 10px sans-serif;
}

.axis path,
.axis line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
}

.tag text {
    text-anchor: end;
}

</style>
<body onLoad="updateData()">
        <div id="option" style="font-size:14px;">
        <form>
        Number of tags:<input value="5" id="nTags_select"></input>
        <input name="updateButton" 
                                 type="button" 
                                value="Update" 
                                onclick="updateData()" />
                        </form>
</div>  
<script src="http://d3js.org/d3.v3.js"></script>
<script>

var margin = {top: 10, right: 10, bottom: 100, left: 40},
    margin2 = {top: 430, right: 10, bottom: 20, left: 40},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom,
    height2 = 500 - margin2.top - margin2.bottom;


var parseDate = d3.time.format("%Y-%m-%d").parse;

var x = d3.time.scale().range([0, width]),
    x2 = d3.time.scale().range([0, width]),
    y = d3.scale.linear().range([height, 0]),
    y2 = d3.scale.linear().range([height2, 0]);

var color = d3.scale.category20();

var xAxis = d3.svg.axis().scale(x).orient("bottom"),
    xAxis2 = d3.svg.axis().scale(x2).orient("bottom"),
    yAxis = d3.svg.axis().scale(y).orient("left");

var brush = d3.svg.brush()
   .x(x2)
  .on("brush", brushed);

var area = d3.svg.area()
    .interpolate("basis")
    .x(function(d) { return x(d.date); })
        .y0(function(d) { return y(d.y0); })
        .y1(function(d) { return y(d.y0 + d.y); });

var area2 = d3.svg.area()
    .interpolate("basis")
    .x(function(d) { return x2(d.date); })  
        .y0(function(d) { return y2(d.y0); })
        .y1(function(d) { return y2(d.y0 + d.y); });

var stack = d3.layout.stack()
        .values(function(d) { return d.values; });



 var oRequest = new XMLHttpRequest();
 var sURL  = "data100.tsv";
 oRequest.open("GET",sURL,false);
 oRequest.setRequestHeader("User-Agent",navigator.userAgent);
 oRequest.send(null)
 var data = oRequest.responseText;

 var data = d3.tsv.parse(data);

    data.forEach(function(d) {
        d.date = parseDate(d.date);
    });

    color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
    var allTags = stack(color.domain().map(function(name) {
        return {
            name: name,
            values: data.map(function(d) {
                return {date: d.date, y: +d[name]};
            })
        };
    }));


    function updateData(){
        var nTags = document.getElementById('nTags_select').value;
        tags=allTags.slice(0,nTags);

    x.domain(d3.extent(data, function(d) { return d.date; }));
    y.domain([0,d3.max(tags, function(d) { return d3.max(d.values, function (d) { return d.y + d.y0; }); })]);
    x2.domain(x.domain());
    y2.domain(y.domain());  

    d3.select("svg").remove();

    var svg = d3.select("body").append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)

    svg.append("defs").append("clipPath")
    .attr("id", "clip")
  .append("rect")
    .attr("width", width)
    .attr("height", height);

var focus = svg.append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var context = svg.append("g")
    .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");

focus.selectAll('path')
            .data(tags)
            .enter()
            .append('path')
            .attr('clip-path','url(#clip)')
            .attr("d", function(d) { return area(d.values); })
            .attr('class','focus')
            .style("fill", function(d) { return color(d.name); });

  focus.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

  focus.append("g")
      .attr("class", "y axis")
      .call(yAxis);


context.selectAll('path')
            .data(tags)
            .enter()
            .append('path')
            .attr('class','context')
            .attr("d", function(d) { return area2(d.values); })
            .style("fill", function(d) { return color(d.name); });


  context.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height2 + ")")
      .call(xAxis2);

  context.append("g")
      .attr("class", "x brush")
      .call(brush)
    .selectAll("rect")
      .attr("y", -6)
      .attr("height", height2 + 7);


  focus.append("text")
            .datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; })
            .attr("transform", function(d) { return "translate(" + x(d.value.date) + "," + y(d.value.y0 + d.value.y / 2) + ")"; })
            .attr("x", -6)
            .attr("dy", "-.35em")
            .text(function(d) { return d.name; });

};




function brushed() {
  x.domain(brush.empty() ? x2.domain() : brush.extent());
  focus.selectAll("path.focus").attr("d", area);
  focus.select(".x.axis").call(xAxis);
}

</script>
1个回答

3
在 Mike 的原始代码中,他选择了名为 focus 的 svg 元素名称,这是不幸的——它 已经存在于 dom 中, 可能会混淆问题。
无论如何,这里的问题主要是范围的问题。
Mike 在获取数据的范围之外定义了 focus,因此将其放入全局范围。因此,它对 brushed() 函数可用。
我要采取的第一步是将 focus 变量的定义移出 updateData() 函数。
然而,还有其他几个问题:
  • You should be using d3.tsv with an anonymous function to get the data. It will make things much simpler.
  • In the brushed function, you need to use an anonymous function and pass the area function d.values, rather than just d. You did this in the updateData function, but not in brushed. i.e.:

    function brushed() {   
        x.domain(brush.empty() ? x2.domain() : brush.extent());
        focus.selectAll("path.focus").attr("d", function(d){return area(d.values)});
        focus.select(".x.axis").call(xAxis);
    }
    
我有一个带刷选功能的图表草稿,链接在这里:http://jsfiddle.net/zQ2EF/。但是,建议认真研究Mike的示例,注意定义位置和作用域,这将有助于您理解问题所在!

谢谢!这非常有帮助。我最初是按照Mike的示例建模的,但是在尝试添加更新功能时遇到了问题。最终我将事情设置为它们原来的样子,以便可以一次加载数据,然后只使用表单元素选择子集进行绘图,清除绘图并重新绘制。通常的“d3.tsv(“blah.tsv”,function(error,data){....};”格式不清楚如何做到这一点。您会将d3.tsv函数放在“updateData()”函数内部还是外部?再次感谢。 - moustachio
好的,在您的iu.edu网站上找到了:http://mypage.iu.edu/~jlorince/projects/tagging/tagExplorer.html - Mark Rajcok
抱歉耽误了 - 我正要发布那个链接。这个代码有点丑陋,并且肯定有很多事情我应该处理得不同,但至少现在它能够运行了。 - moustachio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接