简体   繁体   中英

D3.js radial barplot with bars as lines

I want to build a D3.js radial bar chart. All the example code I've seen shows this sort of chart with the bars plotted as "wedges". Is it possible to tell d3.js to plot bars as straight lines rather than wedges?

This image is an example of how I would like the bars to appear on the chart

r

As an example here is the code that I have that is producing a bar chart.

 const data = [{ "name": "Burj Khalifa", "height": "350" }, { "name": "Shanghai Tower", "height": "263.34" }, { "name": "Abraj Al-Bait Clock Tower", "height": "254.04" }, { "name": "Ping An Finance Centre", "height": "253.20" }, { "name": "Lotte World Tower", "height": "230.16" }, { "name": "Burj Khalifa 2", "height": "350" }, { "name": "Shanghai Tower 2", "height": "263.34" }, { "name": "Abraj Al-Bait Clock Tower 2", "height": "254.04" }, { "name": "Ping An Finance Centre 2", "height": "253.20" } ] data.forEach((d) => { d.height = Number(d.height) }) const heightExtent = d3.extent(data, d => d.height) const bars = data.map(d => d.name) const containerWidth = 400 const containerHeight = 800 const y = d3.scaleLinear() .domain([0, heightExtent[1]]) .range([0, containerHeight]) const x = d3.scaleBand() .domain(bars) .range([0, containerWidth]) .paddingInner(0.02) .paddingOuter(0) const colourScale = d3.scaleOrdinal(d3.schemeBlues[data.length]) const svg = d3.select("#chart-area").append("svg") .attr("width", containerWidth) .attr("height", containerHeight) const buildings = svg.selectAll('rect').data(data) buildings.enter().append('rect') .attr('x', (d, i) => x(d.name)) .attr('y', (d, i) => { return containerHeight - y(d.height) }) .attr('width', x.bandwidth()) .attr('height', (d, i) => y(d.height)) .attr('fill', (d, i) => colourScale(d.name)) const labels = svg.selectAll('text').data(data) labels.enter().append('text') .attr('text-anchor', 'end') .attr('x', (d, i) => (x(d.name) + (x.bandwidth() / 2))) .attr('y', (d, i) => containerHeight - 10) .attr('writing-mode', 'tb') .attr('fill', 'white') .text((d, i) => (d.name + ' - ' + d.height + 'm'))
 <!-- Bootstrap grid setup --> <div class="container"> <div class="row"> <div id="chart-area"></div> </div> </div> <!-- External JS libraries --> <script src="https://d3js.org/d3.v5.min.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>

Consider the following:

 const data = [{ "name": "Burj Khalifa", "height": "350" }, { "name": "Shanghai Tower", "height": "263.34" }, { "name": "Abraj Al-Bait Clock Tower", "height": "254.04" }, { "name": "Ping An Finance Centre", "height": "253.20" }, { "name": "Lotte World Tower", "height": "230.16" }, { "name": "Burj Khalifa 2", "height": "350" }, { "name": "Shanghai Tower 2", "height": "263.34" }, { "name": "Abraj Al-Bait Clock Tower 2", "height": "254.04" }, { "name": "Ping An Finance Centre 2", "height": "253.20" } ] data.forEach((d) => { d.height = Number(d.height) }) const heightExtent = d3.extent(data, d => d.height) const bars = data.map(d => d.name) const containerWidth = 700 const containerHeight = 300 const barWidth = 16 const radius = Math.min(containerWidth / 2, containerHeight / 2) - barWidth * 4; const y = d3.scaleLinear() .domain([0, heightExtent[1]]) .range([0, radius]) const angle = d3.scaleBand() .domain(bars) .range([0, 360]) .paddingInner(0.02) .paddingOuter(0) const colourScale = d3.scaleOrdinal(d3.schemeBlues[data.length]) const svg = d3.select("#chart-area").append("svg") .attr("width", containerWidth) .attr("height", containerHeight) const buildings = svg.selectAll('rect').data(data) buildings.enter().append('rect') .attr('x', - barWidth / 2) .attr('y', barWidth * 2) .attr('width', barWidth) .attr('height', (d, i) => y(d.height)) .style('transform', (d, i) => `translate(${containerWidth / 2}px, ${containerHeight / 2}px) rotate(${angle(d.name)}deg)`) .attr('fill', (d, i) => colourScale(d.name)) const labels = svg.selectAll('text').data(data) labels.enter().append('text') .attr('x', -barWidth / 2) .attr('text-anchor', (d, i) => { const degrees = angle(d.name); if(10 <= degrees && degrees <= 170) { return 'end'; } if(190 <= degrees && degrees <= 350) { return 'start'; } return 'middle'; }) .style('transform', (d, i) => { const xBase = containerWidth / 2; const yBase = containerHeight / 2; const degrees = angle(d.name); const radians = (degrees + 90) / 360 * 2 * Math.PI; const barHeight = y(d.height) + 2.5 * barWidth; const yOffset = Math.sin(radians) * barHeight; const xOffset = Math.cos(radians) * barHeight; return `translate(${xBase + xOffset}px, ${yBase + yOffset}px)`; }) .text((d, i) => (d.name + ' - ' + d.height + 'm'))
 <!-- Bootstrap grid setup --> <div class="container"> <div class="row"> <div id="chart-area"></div> </div> </div> <!-- External JS libraries --> <script src="https://d3js.org/d3.v5.min.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>

I've changed the x scale to return a number between 0 and 360 (the number of degrees rotation), and then place all bars in the middle, rotated by their number of degrees. the only thing is that it's difficult to read rotated text, and even more difficult to rotate text, since its width is unknown. Therefore I showed how to put the text at the end of each bar using sine and cosine functions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM