d3-delaunay线未显示

如何解决d3-delaunay线未显示

我正在尝试在以圆为圆点的线图上实现D3-delaunay。我认为我所做的一切都正确,但是我似乎看不到出现的重叠线。我在这里做错了什么?我正在使用此站点https://observablehq.com/@didoesdigital/9-may-2020-d3-scatterplot-with-voronoi-tooltips)上的代码作为参考,并在我的线路图上实现它。如果此参考文献不合适,则请提供另一个类似于我可以实现的散点图的模板。

enter image description here

class Linechart extends React.Component {
  constructor(props) {
    super(props)
    this.createLineChart = this.createLineChart.bind(this)
  }

  metricToPercent(metric) {
    return (metric / 2 + 0.5) * 100;
  };

  scoreToDescrip(score) {
    if (score >= 0.6) {
      return "Good";
    } else if (score >= 0) {
      return "Average";
    } else {
      return "Poor";
    }
  };

  componentDidMount() {
    this.createLineChart()
  }

  componentDidUpdate() {
    this.createLineChart()
  }

  createLineChart() {
    var margin = {
        top: 85,right: 60,bottom: 60,left: 80
      },width = 960 - margin.left - margin.right,height = 500 - margin.top - margin.bottom;

    var node = this.node
    var divObj = d3.select(node)
    var svgobj = divObj
      .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 + ")");

    //Read the data
    d3.json("https://raw.githubusercontent.com/QamarFarooq/data-for-testing/main/experience_scores.json").then(function(data) {
      var dataArrayWithRemovedEmptyDates = []

      // Remove all elements in the array that dont have companyReviewDate property
      data.forEach(function(d,i) {
        if (d.hasOwnProperty('companyReviewDate')) {
          dataArrayWithRemovedEmptyDates.push(d)
        }
      })

      // Transform `companyReviewDate` into an actual date
      dataArrayWithRemovedEmptyDates.forEach(function(d) {
        d.companyReviewDate = new Date(d.companyReviewDate);
      })

      // Remove all whitespace in CompanyBusinessName,it creates problems 
      data.forEach(function(d,i) {
        d.companyBusinessName = d.companyBusinessName.split(' ').join('');
      })

      // group the data: I want to draw one line per group
      var sumstat = d3.nest()
        .key(function(d) {
          return d.companyBusinessName;
        })
        .entries(dataArrayWithRemovedEmptyDates);

      console.log(sumstat)

      // Define the div for the tooltip
      var tooltip = divObj
        .append("div")
        .attr("class","tooltip")
        .style("position","absolute")
        .style("z-index","10")
        .style("visibility","hidden")
        .style("background-color","white")
        .style("Box-shadow","0 0 4px #000000")
        .style("padding","10px")

      const monthNames = ["January","February","march","April","May","June","July","August","September","October","November","December"
      ];

      var d = new Date();

      tooltip.append("div")
        .attr("class","tooltipDate")
        .html(monthNames[d.getMonth()] + " " + "(" + d.getFullYear() + ")")
        .style("font-size","20px")
        .style("text-align","center")

      tooltip.append("div")
        .attr("class","tooltipName")
        .style("text-align","center")
        .style("color","grey")

      tooltip.append("div")
        .attr("class","tooltipTitle")
        .style("text-align","center")
        .html("Customer Sentiment")
        .style("padding-top","10px")

      tooltip.append("div")
        .attr("class","tooltipscore")
        .style("text-align",'DarkGrey')
        .style("font-size","20px")

      tooltip.append("div")
        .attr("class","tooltipperception")
        .style("text-align","center")


      // Add title for linechart
      svgobj.append("text")
        .attr("text-anchor","end")
        .attr("font-size",25)
        .attr("x",110)
        .attr("y",-50)
        .text("Customer Experience score");

      // Add X axis --> it is a date format
      var x = d3.scaleTime()
        .domain(d3.extent(data,function(d) {
          return d.companyReviewDate;
        }))
        .range([0,width]);
      svgobj.append("g")
        .attr("transform","translate(0," + height + ")")
        .attr("stroke-width","0.3")
        .style("opacity","0.5")
        .call(d3.axisBottom(x).tickSize(-height).tickFormat('').ticks(6))

      // ticks
      svgobj.append("g")
        .style("opacity","0.7")
        .style("font","14px times")
        .attr("transform"," + height + ")")
        .call(d3.axisBottom(x).ticks(5));

      // Add Y axis  
      var y = d3.scaleLinear()
        .domain([0,d3.max(data,function(d) {
          return +d.customerExperiencescore;
        })])
        .range([height,0]);
      svgobj.append("g")
        .attr("stroke-width","0.5")
        .call(d3.axisLeft(y).tickSize(-width).tickFormat('').ticks(5))

      // ticks
      svgobj.append("g")
        .style("opacity","14px times")
        .call(d3.axisLeft(y).ticks(5));

      // Add X axis label:
      svgobj.append("text")
        .attr("text-anchor",20)
        .attr("x",width / 2 + margin.left)
        .attr("y",height + 50)
        .style("fill",d3.color("grey"))
        .text("Company Review Date");

      // Add Y axis label:
      svgobj.append("text")
        .attr("text-anchor",20)
        .attr("transform","rotate(-90)")
        .attr("x",-height / 2 + 40)
        .attr("y",-margin.left + 25)
        .style("fill",d3.color("grey"))
        .text("Customer Experience score")

      // color palette
      var key = sumstat.map(function(d) {
        return d.key
      }) // list of group names

      var color = d3.scaleOrdinal()
        .domain(key)
        .range(['#e41a1c','#377eb8','#4daf4a'])

      // Add one DOT in the legend for each name.
      svgobj.selectAll(".dots")
        .data(key)
        .enter()
        .append("circle")
        .attr("cx",function(d,i) {
          return 250 + i * 120
        })
        .attr("cy",-30)
        .attr("r",7)
        .style("fill",function(d) {
          return color(d)
        })

      // Add LABEL for legends of each dot.
      svgobj.selectAll(".labels")
        .data(key)
        .enter()
        .append("text")
        .style("fill",d3.color("grey"))
        .attr("x",i) {
          return 270 + i * 120
        })
        .attr("y",-28)
        .text(function(d) {
          return d
        })
        .attr("text-anchor","left")
        .style("alignment-baseline","middle")


      // Highlight individual line and show tooltip
      var highlightAndShowTooltip = function(d) {

        // this means i am on top of dot circle
        if (d.key == null) {
          console.log("I am on top of a dot circle of key " + d.companyBusinessName)

          //show tooltip
          tooltip.style("visibility","visible")

          //Data for Tooltip
          tooltip.selectAll(".tooltipName")
            .html(d.key)

          var score = 12 //this will be dynamic,for Now i just set it to 12 to test it out

          tooltip.selectAll(".tooltipscore")
            .html("<span style='color: #cb9f9e;'>" + score + "</span> / 100")

          // first every group turns grey
          svgobj.selectAll(".line")
            .transition().duration(200)
            .style("opacity","0.5")

          svgobj.selectAll(".dot")
            .transition().duration(200)
            .style("opacity","0.5")

          // Second the hovered line takes its color
          svgobj.selectAll("." + d.companyBusinessName)
            .transition().duration(200)
            .style("stroke",color(d.companyBusinessName))
            .style("opacity","1")

          svgobj.selectAll("." + d.companyBusinessName)
            .transition().duration(200)
            .style("stroke","1")
        }

        // this means i am on top of line
        else if (d.companyBusinessName == null) {
          var selected_line = d.key

          console.log("i am on top of line " + d.key)

          // first every group turns grey
          svgobj.selectAll(".line")
            .transition().duration(200)
            .style("opacity","0.5")

          // Second the hovered line takes its color
          svgobj.selectAll("." + selected_line)
            .transition().duration(200)
            .style("stroke",color(selected_line))
            .style("opacity","1")

          svgobj.selectAll("." + selected_line)
            .transition().duration(200)
            .style("stroke","1")

        }
      }

      // UnHighlight and hide tooltip
      var doNotHighlightAndHidetooltip = function(d) {

        //hide tooltip
        tooltip.style("visibility","hidden")

        //return other lines back to normal opacity
        svgobj.selectAll(".line")
          .transition().duration(200).delay(50)
          .style("stroke",function(d) {
            return (color(d.key))
          })
          .style("opacity","1")

        svgobj.selectAll(".dot")
          .transition().duration(200).delay(50)
          .style("stroke",function(d) {
            return (color(d.companyBusinessName))
          })
          .style("opacity","1")
      }

      // keep showing tooltip as cursor moves along line
      var keepShowingTooltip = function(d) {
        tooltip.style("top",(d3.event.pageY - 10) + "px").style("left",(d3.event.pageX + 10) + "px")
      }

      // Draw the line
      svgobj.selectAll(".line")
        .data(sumstat)
        .enter()
        .append("path")
        .attr("class",function(d) {
          return "line " + d.key
        }) // 2 class for each line: 'line' and the group name
        .attr("fill","none")
        .attr("stroke",function(d) {
          return color(d.key)
        })
        .attr("stroke-width",4.5)
        .attr("d",function(d) {
          return d3.line()
            .curve(d3.curveMonotoneX)
            .x(function(d) {
              return x(d.companyReviewDate);
            })
            .y(function(d) {
              return y(+d.customerExperiencescore);
            })
            (d.values)
        });


      // This is the experiment with voronoi
      // Draw dots on points
      svgobj.selectAll(".dot")
        //i am using the raw data array,NOT the nested array
        .data(dataArrayWithRemovedEmptyDates)
        .enter()
        .append("circle")
        .attr("class",i) {
          return i;
        })
        .style("fill","white")
        .style("stroke-width","3px")
        .style("stroke",function(d) {
          return color(d.companyBusinessName)
        })
        .attr("cx",function(d) {
          return x(d.companyReviewDate);
        })
        .attr("cy",function(d) {
          return y(d.customerExperiencescore);
        })
        .attr("r",5.5)

      var voronoi = d3.delaunay
        .from(dataArrayWithRemovedEmptyDates,d => x(d.x),d => y(d.y))
        .voronoi([margin.left,margin.top,width - margin.right,height - margin.bottom]); // ensures voronoi is limited to the

      //Create the voronoi grid
      svgobj.append("g")
        .attr("class","voronoiWrapper")
        .selectAll("path")
        .data(dataArrayWithRemovedEmptyDates)
        .enter()
        .append("path")
        .attr("opacity",0.5)
        .attr("stroke","#ff1493") // Hide overlay
        .attr("fill","none")
        .style("pointer-events","all")
        .attr("d",(d,i) => voronoi.renderCell(i))
        .on("mouSEOver",highlightAndShowTooltip)
        .on("mouSEOut",doNotHighlightAndHidetooltip);
    })

  }
  render() {
    return <div ref = {
      node => this.node = node
    }
    className = "example_div" > < /div>
  }
}

ReactDOM.render( <Linechart / >,document.querySelector('body'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://unpkg.com/d3-delaunay@5.3.0/dist/d3-delaunay.min.js"></script>

解决方法

您对数据结构的了解不够充分:您没有使用d.xd.y,您对它们有自己的名字:

.from(dataArrayWithRemovedEmptyDates,d => x(d.companyReviewDate),d => y(d.customerExperienceScore))

我还从Voronoi删除了边距。原因是svgObj对您而言不是svg,而是经过转换的g。这意味着您已经正确应用了页边距,因此,如果在此处添加页边距,则应两次应用它们。所有这些都会产生以下结果:

class Linechart extends React.Component {
  constructor(props) {
    super(props)
    this.createLineChart = this.createLineChart.bind(this)
  }

  metricToPercent(metric) {
    return (metric / 2 + 0.5) * 100;
  };

  scoreToDescrip(score) {
    if (score >= 0.6) {
      return "Good";
    } else if (score >= 0) {
      return "Average";
    } else {
      return "Poor";
    }
  };

  componentDidMount() {
    this.createLineChart()
  }

  componentDidUpdate() {
    this.createLineChart()
  }

  createLineChart() {
    var margin = {
        top: 85,right: 60,bottom: 60,left: 80
      },width = 960 - margin.left - margin.right,height = 500 - margin.top - margin.bottom;

    var node = this.node
    var divObj = d3.select(node)
    var svgObj = divObj
      .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 + ")");

    //Read the data
    d3.json("https://raw.githubusercontent.com/QamarFarooq/data-for-testing/main/experience_scores.json").then(function(data) {
      var dataArrayWithRemovedEmptyDates = []

      // Remove all elements in the array that dont have companyReviewDate property
      data.forEach(function(d,i) {
        if (d.hasOwnProperty('companyReviewDate')) {
          dataArrayWithRemovedEmptyDates.push(d)
        }
      })

      // Transform `companyReviewDate` into an actual date
      dataArrayWithRemovedEmptyDates.forEach(function(d) {
        d.companyReviewDate = new Date(d.companyReviewDate);
      })

      // Remove all whitespace in CompanyBusinessName,it creates problems 
      data.forEach(function(d,i) {
        d.companyBusinessName = d.companyBusinessName.split(' ').join('');
      })

      // group the data: I want to draw one line per group
      var sumstat = d3.nest()
        .key(function(d) {
          return d.companyBusinessName;
        })
        .entries(dataArrayWithRemovedEmptyDates);

      console.log(sumstat)

      // Define the div for the tooltip
      var tooltip = divObj
        .append("div")
        .attr("class","tooltip")
        .style("position","absolute")
        .style("z-index","10")
        .style("visibility","hidden")
        .style("background-color","white")
        .style("box-shadow","0 0 4px #000000")
        .style("padding","10px")

      const monthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"
      ];

      var d = new Date();

      tooltip.append("div")
        .attr("class","tooltipDate")
        .html(monthNames[d.getMonth()] + " " + "(" + d.getFullYear() + ")")
        .style("font-size","20px")
        .style("text-align","center")

      tooltip.append("div")
        .attr("class","tooltipName")
        .style("text-align","center")
        .style("color","grey")

      tooltip.append("div")
        .attr("class","tooltipTitle")
        .style("text-align","center")
        .html("Customer Sentiment")
        .style("padding-top","10px")

      tooltip.append("div")
        .attr("class","tooltipScore")
        .style("text-align",'DarkGrey')
        .style("font-size","20px")

      tooltip.append("div")
        .attr("class","tooltipPerception")
        .style("text-align","center")


      // Add title for linechart
      svgObj.append("text")
        .attr("text-anchor","end")
        .attr("font-size",25)
        .attr("x",110)
        .attr("y",-50)
        .text("Customer Experience Score");

      // Add X axis --> it is a date format
      var x = d3.scaleTime()
        .domain(d3.extent(data,function(d) {
          return d.companyReviewDate;
        }))
        .range([0,width]);
      svgObj.append("g")
        .attr("transform","translate(0," + height + ")")
        .attr("stroke-width","0.3")
        .style("opacity","0.5")
        .call(d3.axisBottom(x).tickSize(-height).tickFormat('').ticks(6))

      // ticks
      svgObj.append("g")
        .style("opacity","0.7")
        .style("font","14px times")
        .attr("transform"," + height + ")")
        .call(d3.axisBottom(x).ticks(5));

      // Add Y axis  
      var y = d3.scaleLinear()
        .domain([0,d3.max(data,function(d) {
          return +d.customerExperienceScore;
        })])
        .range([height,0]);
      svgObj.append("g")
        .attr("stroke-width","0.5")
        .call(d3.axisLeft(y).tickSize(-width).tickFormat('').ticks(5))

      // ticks
      svgObj.append("g")
        .style("opacity","14px times")
        .call(d3.axisLeft(y).ticks(5));

      // Add X axis label:
      svgObj.append("text")
        .attr("text-anchor",20)
        .attr("x",width / 2 + margin.left)
        .attr("y",height + 50)
        .style("fill",d3.color("grey"))
        .text("Company Review Date");

      // Add Y axis label:
      svgObj.append("text")
        .attr("text-anchor",20)
        .attr("transform","rotate(-90)")
        .attr("x",-height / 2 + 40)
        .attr("y",-margin.left + 25)
        .style("fill",d3.color("grey"))
        .text("Customer Experience Score")

      // color palette
      var key = sumstat.map(function(d) {
        return d.key
      }) // list of group names

      var color = d3.scaleOrdinal()
        .domain(key)
        .range(['#e41a1c','#377eb8','#4daf4a'])

      // Add one DOT in the legend for each name.
      svgObj.selectAll(".dots")
        .data(key)
        .enter()
        .append("circle")
        .attr("cx",function(d,i) {
          return 250 + i * 120
        })
        .attr("cy",-30)
        .attr("r",7)
        .style("fill",function(d) {
          return color(d)
        })

      // Add LABEL for legends of each dot.
      svgObj.selectAll(".labels")
        .data(key)
        .enter()
        .append("text")
        .style("fill",d3.color("grey"))
        .attr("x",i) {
          return 270 + i * 120
        })
        .attr("y",-28)
        .text(function(d) {
          return d
        })
        .attr("text-anchor","left")
        .style("alignment-baseline","middle")


      // Highlight individual line and show tooltip
      var highlightAndShowTooltip = function(d) {

        // this means i am on top of dot circle
        if (d.key == null) {
          console.log("I am on top of a dot circle of key " + d.companyBusinessName)

          //show tooltip
          tooltip.style("visibility","visible")

          //Data for Tooltip
          tooltip.selectAll(".tooltipName")
            .html(d.key)

          var score = 12 //this will be dynamic,for now i just set it to 12 to test it out

          tooltip.selectAll(".tooltipScore")
            .html("<span style='color: #cb9f9e;'>" + score + "</span> / 100")

          // first every group turns grey
          svgObj.selectAll(".line")
            .transition().duration(200)
            .style("opacity","0.5")

          svgObj.selectAll(".dot")
            .transition().duration(200)
            .style("opacity","0.5")

          // Second the hovered line takes its color
          svgObj.selectAll("." + d.companyBusinessName)
            .transition().duration(200)
            .style("stroke",color(d.companyBusinessName))
            .style("opacity","1")

          svgObj.selectAll("." + d.companyBusinessName)
            .transition().duration(200)
            .style("stroke","1")
        }

        // this means i am on top of line
        else if (d.companyBusinessName == null) {
          var selected_line = d.key

          console.log("i am on top of line " + d.key)

          // first every group turns grey
          svgObj.selectAll(".line")
            .transition().duration(200)
            .style("opacity","0.5")

          // Second the hovered line takes its color
          svgObj.selectAll("." + selected_line)
            .transition().duration(200)
            .style("stroke",color(selected_line))
            .style("opacity","1")

          svgObj.selectAll("." + selected_line)
            .transition().duration(200)
            .style("stroke","1")

        }
      }

      // UnHighlight and hide tooltip
      var doNotHighlightAndHideTooltip = function(d) {

        //hide tooltip
        tooltip.style("visibility","hidden")

        //return other lines back to normal opacity
        svgObj.selectAll(".line")
          .transition().duration(200).delay(50)
          .style("stroke",function(d) {
            return (color(d.key))
          })
          .style("opacity","1")

        svgObj.selectAll(".dot")
          .transition().duration(200).delay(50)
          .style("stroke",function(d) {
            return (color(d.companyBusinessName))
          })
          .style("opacity","1")
      }

      // keep showing tooltip as cursor moves along line
      var keepShowingTooltip = function(d) {
        tooltip.style("top",(d3.event.pageY - 10) + "px").style("left",(d3.event.pageX + 10) + "px")
      }

      // Draw the line
      svgObj.selectAll(".line")
        .data(sumstat)
        .enter()
        .append("path")
        .attr("class",function(d) {
          return "line " + d.key
        }) // 2 class for each line: 'line' and the group name
        .attr("fill","none")
        .attr("stroke",function(d) {
          return color(d.key)
        })
        .attr("stroke-width",4.5)
        .attr("d",function(d) {
          return d3.line()
            .curve(d3.curveMonotoneX)
            .x(function(d) {
              return x(d.companyReviewDate);
            })
            .y(function(d) {
              return y(+d.customerExperienceScore);
            })
            (d.values)
        });


      // This is the experiment with voronoi
      // Draw dots on points
      svgObj.selectAll(".dot")
        //i am using the raw data array,NOT the nested array
        .data(dataArrayWithRemovedEmptyDates)
        .enter()
        .append("circle")
        .attr("class",i) {
          return i;
        })
        .style("fill","white")
        .style("stroke-width","3px")
        .style("stroke",function(d) {
          return color(d.companyBusinessName)
        })
        .attr("cx",function(d) {
          return x(d.companyReviewDate);
        })
        .attr("cy",function(d) {
          return y(d.customerExperienceScore);
        })
        .attr("r",5.5)

      var voronoi = d3.Delaunay
        .from(dataArrayWithRemovedEmptyDates,d => y(d.customerExperienceScore))
        .voronoi([0,width,height]); // ensures voronoi is limited to the

      console.log(voronoi,dataArrayWithRemovedEmptyDates);
      //Create the voronoi grid
      svgObj.append("g")
        .attr("class","voronoiWrapper")
        .selectAll("path")
        .data(dataArrayWithRemovedEmptyDates)
        .enter()
        .append("path")
        .attr("opacity",0.5)
        .attr("stroke","#ff1493") // Hide overlay
        .attr("fill","none")
        .style("pointer-events","all")
        .attr("d",(d,i) => {
          return voronoi.renderCell(i);
        })
        .on("mouseover",highlightAndShowTooltip)
        .on("mouseout",doNotHighlightAndHideTooltip);
    })

  }
  render() {
    return <div ref = {
      node => this.node = node
    }
    className = "example_div" > < /div>
  }
}

ReactDOM.render( <Linechart / >,document.querySelector('body'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://unpkg.com/d3-delaunay@5.3.0/dist/d3-delaunay.min.js"></script>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?