How to place Circles(dots) in line chart using “d3.js ” library?

喜欢而已 提交于 2019-12-11 06:34:50

问题


I want to make a line chart using d3.js in which I want to put circle at the data's position. But circle are placed in wrong position in line chart. How can I resolve this ?

I am working in Angular. Basic data set looks like

{
          type: 'Advanced',
          wcpm: 40,
          date: '11-25-2019',
          comment: [
            'Lorem Ipsum Lorem Ipsum Lorem 1',
            'Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum 2'
          ]
 }

Output Image Link: https://ibb.co/pwRdXjx

in which first circle should be placed on Jan25 but it is placed at the wrong position.

class AppComponent implements OnInit {
 title = 'chart';
 margin = { top: 50, right: 50, bottom: 50, left: 50 };
 width = 1200 - this.margin.left - this.margin.right;
 height = 700 - this.margin.top - this.margin.bottom;
 svg: any;
 xScale: any;
 yScale: any;
 line: any;
 toolTipDiv: any;
ngOnInit() {

// XAXIS
this.xScale = d3.scaleBand()
  .domain(this.dataset[0].fluencyData.map((data) => {
    return new Date(data.date);
  }))
  .range([0, this.width]);

// YAXIS
this.yScale = d3.scaleLinear()
  .domain([0, 110])
  .range([this.height, 0]);

// Line Generator
this.line = d3.line()
  .x((d) => this.xScale(new Date(d.date)))
  .y((d) => this.yScale(d.wcpm));
// .curve(d3.curveMonotoneX);

// Add SVG to Div
this.svg = d3.select('#displayChart').append('svg')
  .attr('width', this.width + this.margin.left + this.margin.right)
  .attr('height', this.height + this.margin.top + this.margin.bottom)
  .append('g')
  .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')');

// Define the div for the tooltip
this.toolTipDiv = d3.select('#displayChart').append('div')
  .attr('class', 'tooltip')
  .style('opacity', 0);

// Append XAXIS to the SVG
this.svg.append('g')
  .attr('class', 'xAxis')
  .attr('transform', 'translate(0,' + this.height + ')')
  .call(d3.axisBottom(this.xScale).tickSizeOuter(0).tickFormat(d3.timeFormat('%b %d')));

// Append YAXIS to SVG
this.svg.append('g')
  .attr('class', 'yAxis')
  .call(d3.axisLeft(this.yScale).tickSize(-this.width)
  );

// Make a Path for Dataset
this.svg.append('path')
  .datum(this.dataset[0].fluencyData)
  .attr('class', 'line')
  .attr('d', this.line);

// Text Heading of DATE in chart
this.svg.append('text')
  .attr('transform', 'translate(' + (-20) + ',' + (this.height + 13) + ')')
  .attr('dy', '.35em')
  .attr('class', ' xAxis')
  .text('Date');

// Append Circles in chart with different Colors
this.appendCircles();
}

appendCircles() {
  this.svg.selectAll('.developing')
  .data(this.dataset[0].fluencyData)
  .enter().append('circle')
  .attr('class', (d) => d.type)
  .attr('cx', (d) => this.xScale(new Date(d.date)))
  .attr('cy', (d) => this.yScale(d.wcpm))
  .attr('r', 5)
  .on('mouseover', (d) => {
    this.toolTipDiv.transition()
      .duration(200)
      .style('opacity', .9);
    this.toolTipDiv.html(d.wcpm + '<br/>' + d.type + '<br/>' + d.date)
      .style('left', (d3.event.pageX - 50) + 'px')
      .style('top', (d3.event.pageY - 50) + 'px');
  })
  .on('mouseout', () => {
    this.toolTipDiv.transition()
      .duration(500)
      .style('opacity', 0);
  });
}
}

回答1:


A band scale anticipates that each data item is represented with something having width, such as a bar in a bar chart. The width of the scaled space (the band) for that element is from xBandScale(value) through to xBandScale(value) + xBandScale.bandwidth(). This is handy in that it lets you set the x value of a rectangle to xBandScale(value) and the width to xBandScale.bandwidth().

But if you are positioning circles, you want them to be in the centered in the middle of this space. You are positioning your points with xScale(value), which is the left hand side of the band:

The axis ticks are offset because they anticipate the band's width, afterall we are passing a band scale to the axis generator.

Yes, you could position your circles with:

.attr("cx", function(d) { return xScale(d.someProperty) + xScale.bandwidth()/2 })

This will place the circle in the middle of the band - which aligns your circles with your axis ticks. But, your line won't start at the vertical axis, and this isn't really the intended purpose of the band scale, even though it works.

Instead, we could try d3.scalePoint - which is also an ordinal scale and similar to a band scale. The docs state: "Point scales are typically used for scatterplots with an ordinal or categorical dimension." They are ideal where you have objects that don't have width (or more accurately, are positioned in the middle of the band).

With this we get a graph that looks like (using the same data and structure as above):

Note that the spacing between points is xScale.step(), the axis ticks have moved (also, the last step exceeds the axis line - it is just for illustration, the axis ends at d).

Lastly, if we have an axis that represents something like time or some other contiuous quantity, we can use any of the continuous scales in d3 as well rather than an ordinal scale, which may often be preferable.

For simplicity, I'm assuming no padding to keep the images easier to read




回答2:


You have a margin-left of 50 in your SVG but your xScale range is starting at 0. That's why everything is shifted to the left. Your xScale range should start at at your margin and end at the svg width.

this.xScale = d3.scaleBand()
  .domain(this.dataset[0].fluencyData.map((data) => {
    return new Date(data.date);
  }))
  .range([this.margin.left, this.width]);


来源:https://stackoverflow.com/questions/55357442/how-to-place-circlesdots-in-line-chart-using-d3-js-library

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!