Home > Mobile >  Failed to display lines in a line chart in D3js
Failed to display lines in a line chart in D3js

Time:12-09

I have three array of objects.

I want to create a chart with 3 lines, the Dates are the same for the three arrays. The Y for peakUsage and avgUsage are the same values so they are horizontal straight lines

This code show the Y and X Axises but failing with drawing the actual lines.

data: { x: Date; y: number }[],
peakUsage: { x: Date; y: number }[],
avgUsage: { x: Date; y: number }[]

const axisThickness = 60;
  const width = (params.width || 500) - axisThickness;
  const height = (params.height || 500) - axisThickness;

  const { document } = new JSDOM('').window;

  // Create D3 container with SVG element
  const div = d3.select(document.body).append('div');
  const svg = div
    .attr('class', 'container')
    .append('svg')
    .attr('xmlns', 'http://www.w3.org/2000/svg')
    .attr('width', width   axisThickness)
    .attr('height', height   axisThickness);

  const yScale = d3.scaleLinear().range([height, 0]);

  yScale.domain([0, Math.max(...data.map((d) => d.y))]);

  const xScale = d3
    .scaleTime()
    .range([0, width])
    .domain(
      d3.extent(data, function (d) {
        return new Date(d.x);
      }) as Date[]
    );

  const g = svg.append('g').attr('transform', `translate(${axisThickness},${axisThickness / 2})`);

  g.append('g').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale));

  g.append('g')
    .call(
      d3
        .axisLeft(yScale)
        .tickFormat(function (d) {
          return `${d}`; // Label text
        })
        .ticks(8)
    )
    .append('text')
    .attr('transform', 'rotate(-90)') 
    .attr('y', 6)
    .attr('dy', '-5.1em')
    .attr('text-anchor', 'end')
    .attr('fill', 'black')
    .text('Est. kWh');

  g.selectAll('.line')
    .enter()
    .append('path')
    .data(data)
    .attr('fill', 'none')
    .attr('stroke', 'green')
    .attr('stroke-width', 1.5)
    .attr('d', function (d) {
      return d3
        .line()
        .x(function (d: any) {
          return xScale(d.x);
        })
        .y(function (d: any) {
          return yScale(d.y);
        }) as any;
    });

  g.selectAll('.line')
    .enter()
    .append('path')
    .data(peakUsage)
    .attr('fill', 'none')
    .attr('stroke', 'Orange')
    .attr('stroke-width', 1.5)
    .attr(
      'd',
      d3
        .line()
        .x(function (d: any) {
          return xScale(d.x);
        })
        .y(function (d: any) {
          return yScale(d.y);
        }) as any
    );
  g.selectAll('.line')
    .enter()
    .append('path')
    .data(avgUsage)
    .attr('fill', 'none')
    .attr('stroke', 'blue')
    .attr('stroke-width', 1.5)
    .attr(
      'd',
      d3
        .line()
        .x(function (d: any) {
          return xScale(d.x);
        })
        .y(function (d: any) {
          return yScale(d.y);
        }) as any
    );

What I am doing wrong?

CodePudding user response:

This bit is in the wrong order:

g.selectAll('.line')
    .enter()
    .append('path')
    .data(data)

It should be:

g.selectAll('.line')
    .data(data)
    .enter()
    .append('path')

But that does not fix your issue. Since you are using 3 different arrays for each line individually (a better approach is using just one array with 3 inner arrays, but that's another issue...), you can simply declare the line generator...

const lineGenerator = d3.line()
    .x(function (d: any) {
      return xScale(d.x);
    })
    .y(function (d: any) {
      return yScale(d.y);
    })

... and then, for each path, do:

g.append('path')
    .attr('fill', 'foo')
    .attr('stroke', 'bar')
    .attr('stroke-width', 'baz')
    .attr('d', lineGenerator(pathDataHere))
    //the data for each line -----^
  • Related