【问题标题】:d3 multiline voronoi not workingd3多线voronoi不工作
【发布时间】:2016-04-29 06:25:03
【问题描述】:

我创建了一个多折线图,但我一直在努力添加一个 voronoi 以获得更好的鼠标交互。任何帮助我指出正确方向的帮助将不胜感激。

这是我的数据:

U.S. Custom Ports,Coast,Year,Value
"Aberdeen, WA",P,1997,0
"Aberdeen, WA",P,1998,24
"Aberdeen, WA",P,1999,3284.95
"Aberdeen, WA",P,2000,0
"Aberdeen, WA",P,2001,78.83
"Aberdeen, WA",P,2002,75.16
"Aberdeen, WA",P,2003,0
"Aberdeen, WA",P,2004,0
"Aberdeen, WA",P,2005,194.35
"Aberdeen, WA",P,2006,0
"Aberdeen, WA",P,2007,0
"Aberdeen, WA",P,2008,0
"Aberdeen, WA",P,2009,45.14
"Aberdeen, WA",P,2010,303
"Aberdeen, WA",P,2011,680.42
"Aberdeen, WA",P,2012,437.43
"Aberdeen, WA",P,2013,2436.97
"Aberdeen, WA",P,2014,894.86
"Anacortes, WA",P,1997,0
"Anacortes, WA",P,1998,0
"Anacortes, WA",P,1999,0
"Anacortes, WA",P,2000,0
"Anacortes, WA",P,2001,0
"Anacortes, WA",P,2002,0
"Anacortes, WA",P,2003,0
"Anacortes, WA",P,2004,0
"Anacortes, WA",P,2005,0

这是我的代码:

function drawMultiLine(){

      var container = d3.select('#g-multiline');

      var margin = {top:10, right: 80, bottom: 30, left: 30},
      width = 300 - margin.left - margin.right,
      height = 550 - margin.top - margin.bottom;

      var parseYear = d3.time.format("%Y").parse;


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

      var xAxis = d3.svg.axis().scale(x)
          .orient("bottom")
          .ticks(2)

      var yAxis = d3.svg.axis().scale(y)
          .orient("right").ticks(4)

      var valueline = d3.svg.line()
        // .interpolate("basis") 
        .interpolate("step-before")
        .x(function(d) { return x(d.Year); })
        .y(function(d) { return y(d.Value); });

      var g = container
        .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append('g')
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

      function make_x_axis() { return d3.svg.axis()
        .scale(x)
        .orient("bottom")
        .ticks(5)
      }
      function make_y_axis() { return d3.svg.axis()
        .scale(y)
        .orient("left")
        .ticks(10)
      }

      var voronoi = d3.geom.voronoi()
        .x(function(d) { return x(d.Year); })
        .y(function(d) { return y(d.Value); })
        .clipExtent([[-margin.left, -margin.top], [width + margin.right, height + margin.bottom]]);

      d3.csv("../../data/container_data.csv", function(error,data){
        if (error) throw error; 

        data.forEach(function(d) {
          d.Year = parseYear(d.Year);
          d.Value = +d.Value;
        });

        x.domain(d3.extent(data, function(d) { return d.Year; }));
        y.domain([0, d3.max(data, function(d) { return d.Value; })]); 

        g.append("g")
          .attr("class", "xgrid")
          .attr("transform", "translate(0," + height + ")")
          .call(make_x_axis()
              .tickSize(-height, 0, 0)

          )
        g.append("g")
          .attr("class", "ygrid")
          .call(make_y_axis()
              .tickSize(-width, 0, 0)
              .ticks(5)
              .tickFormat(d3.format("s"))
          )

        g.append("g")         // Add the X Axis
          .attr("class", "x axis")
          .attr("transform", "translate(0," + height + ")")
          .call(xAxis);

        g.append("g")         // Add the Y Axis
          .attr("class", "y axis")
          .call(yAxis)
          .append("text")
          .attr("y", 80)
          .attr("dy", ".71em")
          .style("text-anchor", "end")
          .text("Tons");


         // Nest the entries by port name
        var dataNest = d3.nest()
            .key(function(d){return d["U.S. Custom Ports"]})
            .entries(data);

        g.append("rect")
              .attr("x", 110)
              .attr("y", 0)
              .attr("width", "5%")
              .attr("height", height)
              .attr("class","recession")

        dataNest
          .forEach(function(d) {
            g.append("path")
              .attr("class", "line")
              .attr("d", valueline(d.values))
              // .attr("class",d.key, "line")
        });

        var focus = g.append("g")
          .attr("class", "focus")
          .attr("transform", "translate(-100,-100)");

        focus.append("circle")
          .attr("r", 4);

        var voronoiGroup = g.append("g")
          .attr("class", "voronoi");

        voronoiGroup.selectAll("path")
          .data(voronoi(dataNest))
          .enter().append("path")
          .attr("d", function(d) { return "M" + d.join("L") + "Z"; })
          .datum(function(d) { return d.point; })


      })
    }

drawMultiLine();

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    您正在向voronoi 函数传递一个嵌套对象数组。这是不正确的,因为它是expecting a flat array of data。最简单的扁平化方法是:

    var vd = [];
    dataNest.forEach(function(d){
      vd = vd.concat(d.values);
    });
    
    voronoiGroup.selectAll("path")
      .data(voronoi(vd))
      .enter().append("path")
      .attr("d", function(d) {
        if (d) return "M" + d.join("L") + "Z";
       })
       .datum(function(d) {
          if (d) return d.point;
       });
    

    工作代码:

    <!DOCTYPE html>
    <html>
    
    <head>
      <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
      <style>
        path {
          fill: none;
          stroke: steelblue;
        }
        
        rect {
          fill: orange;
        }
        
        .voronoi path {
      fill: none;
      pointer-events: all;
    }
      </style>
    </head>
    
    <body>
      <div id="g-multiline"></div>
      <script>
        function drawMultiLine() {
    
          var container = d3.select('#g-multiline');
    
          var margin = {
              top: 10,
              right: 80,
              bottom: 30,
              left: 30
            },
            width = 500 - margin.left - margin.right,
            height = 550 - margin.top - margin.bottom;
    
          var parseYear = d3.time.format("%Y").parse;
    
    
          var x = d3.time.scale().range([0, width]);
          var y = d3.scale.linear().range([height, 0]);
    
          var xAxis = d3.svg.axis().scale(x)
            .orient("bottom")
            .ticks(2)
    
          var yAxis = d3.svg.axis().scale(y)
            .orient("right").ticks(4)
    
          var valueline = d3.svg.line()
            // .interpolate("basis") 
            .interpolate("step-before")
            .x(function(d) {
              return x(d.Year);
            })
            .y(function(d) {
              return y(d.Value);
            });
    
          var g = container
            .append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append('g')
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
          function make_x_axis() {
            return d3.svg.axis()
              .scale(x)
              .orient("bottom")
              .ticks(5)
          }
    
          function make_y_axis() {
            return d3.svg.axis()
              .scale(y)
              .orient("left")
              .ticks(10)
          }
    
          var voronoi = d3.geom.voronoi()
            .x(function(d) {
              return x(d.Year);
            })
            .y(function(d) {
              return y(d.Value);
            })
            .clipExtent([
              [-margin.left, -margin.top],
              [width + margin.right, height + margin.bottom]
            ]);
    
          //d3.csv("data.csv", function(error, data) {
            var data = [{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"1997","Value":"0"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"1998","Value":"24"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"1999","Value":"3284.95"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2000","Value":"0"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2001","Value":"78.83"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2002","Value":"75.16"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2003","Value":"0"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2004","Value":"0"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2005","Value":"194.35"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2006","Value":"0"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2007","Value":"0"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2008","Value":"0"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2009","Value":"45.14"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2010","Value":"303"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2011","Value":"680.42"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2012","Value":"437.43"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2013","Value":"2436.97"},{"U.S. Custom Ports":"Aberdeen, WA","Coast":"P","Year":"2014","Value":"894.86"},{"U.S. Custom Ports":"Anacortes, WA","Coast":"P","Year":"1997","Value":"0"},{"U.S. Custom Ports":"Anacortes, WA","Coast":"P","Year":"1998","Value":"0"},{"U.S. Custom Ports":"Anacortes, WA","Coast":"P","Year":"1999","Value":"0"},{"U.S. Custom Ports":"Anacortes, WA","Coast":"P","Year":"2000","Value":"0"},{"U.S. Custom Ports":"Anacortes, WA","Coast":"P","Year":"2001","Value":"0"},{"U.S. Custom Ports":"Anacortes, WA","Coast":"P","Year":"2002","Value":"0"},{"U.S. Custom Ports":"Anacortes, WA","Coast":"P","Year":"2003","Value":"0"},{"U.S. Custom Ports":"Anacortes, WA","Coast":"P","Year":"2004","Value":"0"},{"U.S. Custom Ports":"Anacortes, WA","Coast":"P","Year":"2005","Value":"0"}];
    
            data.forEach(function(d) {
              d.Year = parseYear(d.Year);
              d.Value = +d.Value;
            });
    
            x.domain(d3.extent(data, function(d) {
              return d.Year;
            }));
            y.domain([0, d3.max(data, function(d) {
              return d.Value;
            })]);
    
            g.append("g")
              .attr("class", "xgrid")
              .attr("transform", "translate(0," + height + ")")
              .call(make_x_axis()
                .tickSize(-height, 0, 0)
    
              )
            g.append("g")
              .attr("class", "ygrid")
              .call(make_y_axis()
                .tickSize(-width, 0, 0)
                .ticks(5)
                .tickFormat(d3.format("s"))
              )
    
            g.append("g") // Add the X Axis
              .attr("class", "x axis")
              .attr("transform", "translate(0," + height + ")")
              .call(xAxis);
    
            g.append("g") // Add the Y Axis
              .attr("class", "y axis")
              .call(yAxis)
              .append("text")
              .attr("y", 80)
              .attr("dy", ".71em")
              .style("text-anchor", "end")
              .text("Tons");
    
    
            // Nest the entries by port name
            var dataNest = d3.nest()
              .key(function(d) {
                return d["U.S. Custom Ports"]
              })
              .entries(data);
    
            g.append("rect")
              .attr("x", 110)
              .attr("y", 0)
              .attr("width", "5%")
              .attr("height", height)
              .attr("class", "recession")
    
            dataNest
              .forEach(function(d) {
                g.append("path")
                  .attr("class", "line")
                  .attr("d", valueline(d.values))
                  .attr("id", d.key.replace(/, /, ''))
              });
    
            var focus = g.append("g")
              .attr("class", "focus")
              .attr("transform", "translate(-100,-100)");
    
            focus.append("circle")
              .attr("r", 4);
    
            var voronoiGroup = g.append("g")
              .attr("class", "voronoi");
            
            var vd = [];
            dataNest.forEach(function(d){
              console.log(d);
              vd = vd.concat(d.values);
            })
    
            voronoiGroup.selectAll("path")
              .data(voronoi(vd))
              .enter().append("path")
              .attr("d", function(d) {
                if (d) return "M" + d.join("L") + "Z";
              })
              .datum(function(d) {
                if (d) return d.point;
              })
              .on("mouseover", function(d){
                var key = d["U.S. Custom Ports"].replace(/, /,'');
                d3.select('#' + key)
                  .style("stroke", "green");
              })
              .on("mouseout", function(d){
                var key = d["U.S. Custom Ports"].replace(/, /,'');
                d3.select('#' + key)
                  .style("stroke", "steelblue");
              })
    
    
         // })
        }
    
        drawMultiLine();
      </script>
    </body>
    
    </html>

    【讨论】:

    • 哇,谢谢马克,这非常有帮助。我确实有一个简单的问题,我正在尝试添加鼠标悬停/鼠标悬停功能来更改线条颜色.....但到目前为止,我还没有运气让鼠标悬停来对应我的线条。我很确定是我的 d3 选择导致了问题,但我不知道如何解决它。
    • 很抱歉再次打扰您。我厌倦了上面的 sn-p,但仍然没有运气让悬停颜色起作用。我很困惑。我将我的代码上传到 bl.ocks,如果您能指导我正确的方向,那将非常有帮助:blockbuilder.org/jeremycflin/a304ed19652fee4cfbe58bc9c0afa2ec
    • @fabrice,在这里看到它:blockbuilder.org/anonymous/d097eb132f5b46a3f22315d760f87242,你错过了一些 css(voronoi 上的指针事件)并为每一行分配了一个唯一的 id。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-16
    • 1970-01-01
    • 2017-09-11
    • 1970-01-01
    • 2018-05-14
    • 2017-05-01
    • 1970-01-01
    相关资源
    最近更新 更多