如何解决d3-delaunay线未显示
我正在尝试在以圆为圆点的线图上实现D3-delaunay。我认为我所做的一切都正确,但是我似乎看不到出现的重叠线。我在这里做错了什么?我正在使用此站点(https://observablehq.com/@didoesdigital/9-may-2020-d3-scatterplot-with-voronoi-tooltips)上的代码作为参考,并在我的线路图上实现它。如果此参考文献不合适,则请提供另一个类似于我可以实现的散点图的模板。
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.x
和d.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 举报,一经查实,本站将立刻删除。